ArchR takes as input aligned BAM or fragment files. These files are stored in the HDF5 file format (hierarchical data format version5). The HDF5 files are the constituent pieces of an ArchR analysis. They are called Arrow files. All Arrow files are grouped into a project, a compressed R data file. The Files are accessed in minimal chunks (parallel read and write operations). Therefore, in memory we do not have any large file sizes.

To select high quality cells TSS enrichment scores are used.

First steps in ArchR

Load libraries

library(ArchR)
library(knitr)
library(rhdf5)
library(uwot)
library(tidyverse)
#library(caret)
h5disableFileLocking()
inputFiles <- getTutorialData("Hematopoiesis")
addArchRGenome("hg19")

Create Arrow Files

  1. read accessible fragments
  2. calculate QC information for each cell (TSS enrichment scores and nucelosome info)
  3. filter cells based on QC parameters
  4. create genome-wide tile matrix using 500-bp bins
  5. create GeneScoreMatrix using gene annotation
ArrowFiles <- createArrowFiles(
  inputFiles = inputFiles,
  sampleNames = names(inputFiles),
  minTSS = 4, #Dont set this too high because you can always increase later
  minFrags = 1000, # minimum number of mapped fragments required
  # count matrix, instead of using peak it uses fixed-width sliding window of bins across the whole genome
  addTileMat = TRUE, 
  addGeneScoreMat = TRUE, # uses signal proximal to the TSS to estimate gene activity
  subThreading = FALSE,
  maxFrags = 1e+05,
  minFragSize = 10,
  maxFragSize = 2000,
  QCDir = "QualityControl",
  # the length in bp that wraps around nucleosomes -> 
    #identify fragments as sub-nucleosome spanning, mono-nucleosome spanning or multi-nucleosome spanning
  nucLength = 147, 
  # integer vector -> define region up/downstream of TSS to include as promoter region
  # can be used to calculate e.g fraction of reads in promoter (FIP)
  promoterRegion = c(2000, 100),
  # parameters for computing TSS enrichment scores, window (bp) centered at TSS = 101
  # flanking window = 2000 bp
  # norm = size of flank window used for normalization = 100 bp
  # accessibility within 101 bp surrounding the TSS will be normalized to accessibility
  # in 100 bp bins from -2000:-1901bp and 1901: 2000
  TSSParams = list(101, 2000, 100),
  # which chromosomes to exclude form downstream analysis
  # in human and mouse: mitochondrial DNA (chrM) and male sex chromosome (chrY)
  # the fragments are still stored in the arrow files
  excludeChr = c("chrM", "chrY"),
  # number of chunks to divide chromosomes in -> low-memory parallelized reading of input files
  nChunk = 5,
  # name of field in input bam file containing the barcode tag information
  bcTag = "qname",
  offsetPlus = 4, # offset applied to + stranded Tn5 insertion -> account for precise Tn5 binding site
  offsetMinus = -5, 
  logFile = createLogFile("createArrows")
)
# the arrow files object simply contains a character vector of arrow file paths
ArrowFiles
## [1] "scATAC_BMMC_R1.arrow"      "scATAC_CD34_BMMC_R1.arrow"
## [3] "scATAC_PBMC_R1.arrow"

Quality Control

Have a look at this for additional QC! https://bioconductor.org/packages/devel/bioc/vignettes/ATACseqQC/inst/doc/ATACseqQC.html

  1. the number of unique nuclear fragments (as opposed to mitochondrial fragments) A cell with very few usable fragments will not provide enough data to mak useful conclusions.
  2. signal-to-background ratio -> if this is low this probably corresponds to dying cells where the entire genome allows random transposition
  3. fragment size distribution -> since 147 bp are wrapped around a nucleosome it is expected that there are depletions of fragments of this length at regular intervals. We expect to see a periodic distribution of fragmetn size corresponding to nucleosomes (mono, di, tri, …), because Tn5 cannot cut DNA that is tightly wrapped around a nucleosome.

TSS enrichment score

= signal to noise calculation

ATAC-seq data is enriched at TSS regions compared to other genomic regions, because large protein complexes bind to promoters. Therefore, there is a local enrichment of per-basepair accessibility relative to flanking regions. The score is approximated for each cell. The average accessibility within 50 bp of the TSS is divided by the average accessibility of the TSS flanking positions.

The reads around a particular TSS are collected and aggregated to form a distribution of reads which is centered on the TSS. This distribution extends to 1000bp in both directions * we take the average read depth in the 100 bps at each end flank and calcualte a fold change at each position over that average read depth (total of 200bp of averaged data) * If there is a high read signal at TSS there will be an increase in signal towards the middle of the distribution * high signal means that the region is accessible * the signal value at the center of the distribution is the TSS enrichment metric

Inferring Doublets

Should be removed, because they can interfere with downstream analysis.

Doublet detection-and-removal algorithm: Heterotypic doublets are identified by generating a collection of synthetic doublets. These synthetic doublets are projected into low-dimensional embeddings. Searching for nearest neighbours to the synthetic doublets we can identify doublets in the dataset. This outperforms the prediction of doublets using fragment number (ROC-AUC). (Compared to demuxlet as ground truth)

We can also identify doublets in the scRNA-seq space if we have paired data and remove the cells in this way.

# for each sample provided doublet information will be assigned to each cell
# this way we can remove doublet-based clusters downstream
doubScores <- addDoubletScores(
  useMatrix = "TileMatrix",
  input = ArrowFiles,
  k = 10, #Refers to how many cells near a "pseudo-doublet" to count.
  nTrial = 5, # number of time to simulate nCell doublets 
  knnMethod = "UMAP", #Refers to the dimensionality reduciton method to use for nearest neighbor search.
  LSIMethod = 1 # oder of normalization: tf-log(idf)
)
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

# the doublet information is saved in a simpleListobject
# read in the object
doublet_summary <- readRDS("QualityControl/scATAC_BMMC_R1/scATAC_BMMC_R1-Doublet-Summary.rds")

doublet_summary[[2]] %>% head() %>% kable()
x y density type color
doublet_14733 5.8904443 -3.702020 0.0006007 simulated_doublet 0.0006007
doublet_14467 5.5716429 -3.579292 0.0006255 simulated_doublet 0.0006255
doublet_4584 5.4819946 -3.693586 0.0006264 simulated_doublet 0.0006264
doublet_14409 0.6149653 3.274105 0.0009510 simulated_doublet 0.0009510
doublet_12622 6.3805246 -3.755051 0.0010196 simulated_doublet 0.0010196
doublet_5702 5.2130489 -3.519199 0.0010345 simulated_doublet 0.0010345

# get first entry of the list = originalDataUMAP
p1 <- doublet_summary [[1]] %>% 
  ggplot() +
  geom_point(aes(x = X1, y = X2, col = enrichment), size = .1) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="DoubletEnrichment")) +
  labs(title = "Simulated Doublet Enrichment over expectation")

p2 <- doublet_summary [[1]] %>% 
  ggplot() +
  geom_point(aes(x = X1, y = X2, col = score), size = .1) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="DoubletScores -log10(P-adj)")) +
  labs(title = "Doublet Scores -log10(P-adj)")


p3 <- doublet_summary[[2]] %>% 
  ggplot() +
  geom_point(aes(x = x, y = y, col = density), size = .1) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="Simulated Doublet Density")) +
  labs(title = "Doublet density")


p4 <- doublet_summary[[2]] %>% 
  ggplot() +
  geom_point(aes(x = x, y = y, col = density), size = .1) +
  geom_point(data = doublet_summary[[1]], aes(x = X1, y = X2), size = .1, alpha = .4) +
  scale_color_viridis_c() +
  guides(fill=guide_legend(title="Simulated Doublet Density")) +
  labs(title = "Simulated doublet density overlayed")

gridExtra::grid.arrange(p1, p2, p3, p4, ncol = 2)

Create ArchRProject

An ArchR Project is initialized with some important attributes:

  • ouput directory
  • sample names
  • sampleColData -> matrix containint data for each sample
  • cellColData -> contains data associated with each cell
    • after using addDoubletScore() there will be a column for “Doublet Enrichment” and “Doublet Score”
  • total number of cells (excluding doublets)
  • median TSS score & median number of fragments across all cells and samples
proj <- ArchRProject(
  ArrowFiles = ArrowFiles, 
  outputDirectory = "ArchRVignette",
  copyArrows = TRUE, #This is recommened so that you maintain an unaltered copy for later usage.
  geneAnnotation = getGeneAnnotation(),
  #genomeAnnotation = getGeneAnnotation(),
  showLogo = FALSE
)

Access ArchR:

Cell Names:

# cell names
proj$cellNames[1:5]
## [1] "scATAC_BMMC_R1#TTATGTCAGTGATTAG-1" "scATAC_BMMC_R1#AAGATAGTCACCGCGA-1"
## [3] "scATAC_BMMC_R1#GCATTGAAGATTCCGT-1" "scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1"
## [5] "scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1"

Sample Names:

proj$Sample[1:5]
## [1] "scATAC_BMMC_R1" "scATAC_BMMC_R1" "scATAC_BMMC_R1" "scATAC_BMMC_R1"
## [5] "scATAC_BMMC_R1"
length(proj$Sample)
## [1] 10660

For each cell there is a TSS enrichment score: Is this the average TSS enrichment score for each cell?

# TSS Enrichment score
proj$TSSEnrichment %>% head
## [1] 7.204 7.949 4.447 6.941 4.771 9.185
quantile(proj$TSSEnrichment)
##      0%     25%     50%     75%    100% 
##  4.1090 13.9255 16.8150 19.9285 41.9800



hist(proj$TSSEnrichment, xlab = "TSS enrichment score", main = "TSS Enrichment Score", breaks = 100)


# reads in TSS
hist(proj$ReadsInTSS, xlab = "Reads in TSS", main = "Reads in TSS", breaks = 100) 

hist(proj$ReadsInPromoter, xlab = "Reads in Promoter", main = "Reads in Promoter", breaks = 100)

hist(proj$ReadsInBlacklist, xlab = "Reads in Blcklist", main ="Reads in Blacklist", breaks = 100)

number of fragments:

# number of fragmetns for each cell
proj$nFrags %>% head
## [1] 26189 20648 18991 18296 17458 17109
quantile(proj$nFrags)
##       0%      25%      50%      75%     100% 
##  1001.00  2150.00  3049.50  4332.25 91194.00


p1 <- ggplot(as_tibble(proj$nFrags), aes(x = value)) + geom_histogram(binwidth = 100) +
  labs(title = "number of unique fragments") +
  xlab("number of unique fragments")
p2 <- ggplot(as_tibble(proj$nMultiFrags), aes(x = value)) + geom_histogram(binwidth = 100) +
  labs(title = "number of mulit-nucleosome fragments") +
  xlab("number of mulit-nucleosome fragments")
p3 <- ggplot(as_tibble(proj$nMonoFrags), aes(x = value)) + geom_histogram(binwidth = 100) +
  labs(title = "number of mono-nucleosome fragments") +
  xlab("number of mono-nucelosome fragments")
p4 <- ggplot(as_tibble(proj$nDiFrags), aes(x = value)) + geom_histogram(binwidth = 100) +
  labs(title = "number of Di-nucleosome fragments") +
  xlab("number Di-fragments")
gridExtra::grid.arrange(p1, p2, p3,p4, ncol = 2)

How to subset an ArchR project

  • numerically
# note that that only 100 cells are accessed
proj[1:100,]
## class: ArchRProject 
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette 
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
##   BlacklistRatio
## numberOfCells(1): 100
## medianTSS(1): 10.794
## medianFrags(1): 10200.5
  • by cell name
proj[proj$cellNames[1:100], ]
## class: ArchRProject 
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette 
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
##   BlacklistRatio
## numberOfCells(1): 100
## medianTSS(1): 10.794
## medianFrags(1): 10200.5
  • keep specific samples
idxSample <- BiocGenerics::which(proj$Sample %in% "scATAC_BMMC_R1")
cellsSample <- proj$cellNames[idxSample]
proj[cellsSample, ]
## class: ArchRProject 
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette 
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
##   BlacklistRatio
## numberOfCells(1): 4932
## medianTSS(1): 15.2575
## medianFrags(1): 2771
  • specific cutoff for TSS enrichment score
idxPass <- which(proj$TSSEnrichment >= 8)
cellsPass <- proj$cellNames[idxPass]
proj[cellsPass, ]
## class: ArchRProject 
## outputDirectory: /omics/groups/OE0533/internal/katharina/scDoRI/ArchR/ArchRVignette 
## samples(3): scATAC_BMMC_R1 scATAC_CD34_BMMC_R1 scATAC_PBMC_R1
## sampleColData names(1): ArrowFiles
## cellColData names(15): Sample TSSEnrichment ... DoubletEnrichment
##   BlacklistRatio
## numberOfCells(1): 10499
## medianTSS(1): 16.899
## medianFrags(1): 3042
getAvailableMatrices(proj)
## [1] "GeneScoreMatrix" "TileMatrix"

Obtaining columns from cellColData

  • retrieve metadata columns, e.g. number of unique nuclear fragments per cell
df <- getCellColData(proj, select = "nFrags")
df %>% head %>% kable()
nFrags
scATAC_BMMC_R1#TTATGTCAGTGATTAG-1 26189
scATAC_BMMC_R1#AAGATAGTCACCGCGA-1 20648
scATAC_BMMC_R1#GCATTGAAGATTCCGT-1 18991
scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1 18296
scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1 17458
scATAC_BMMC_R1#AGTTACGAGAACGTCG-1 17109
  • operations on a given column
df <- getCellColData(proj, select = c("log10(nFrags)", "nFrags -1 "))

df %>% head %>% kable()
log10.nFrags. nFrags..1.
scATAC_BMMC_R1#TTATGTCAGTGATTAG-1 4.418119 26188
scATAC_BMMC_R1#AAGATAGTCACCGCGA-1 4.314878 20647
scATAC_BMMC_R1#GCATTGAAGATTCCGT-1 4.278548 18990
scATAC_BMMC_R1#TATGTTCAGGGTTCCC-1 4.262356 18295
scATAC_BMMC_R1#TCCATCGGTCCCGTGA-1 4.241994 17457
scATAC_BMMC_R1#AGTTACGAGAACGTCG-1 4.233225 17108

Plot QC metrics

Data before QC and corresponding plots are saved in the Quality Control output folder.

log10(unique fragments) vs TSS enrichment

  • TSS enrichment score = signal-to-background
  • number of unique fragments -> cells with very few fragments do not have enough data to confidently analyze them
  • in the plot areas with more points/cells are colored in orange, and areas with less points in blue, indicating the distribution of cell
#create 3 separate dataframes for all samples
three_samples <- map(unique(proj$Sample), function(name){
  index <- BiocGenerics::which(proj$Sample %in% name)
  cells <- proj$cellNames[index]
  sample_subset <- proj[cells]
  df <- getCellColData(sample_subset, select = c("log10(nFrags)", "TSSEnrichment"))
  p <- ggPoint(
    x = df[, 1], y = df[, 2], 
    colorDensity = TRUE, # should the density of points on the plot be indicated by color?
    continuousSet = "sambaNight", 
    xlabel = "Log10 unique fragments",
    ylabel = "TSS enrichment",
    title = paste0("Sample: ", name),
    xlim = c(log10(500), quantile(df[,1], probs = 0.99)),
    ylim = c(0, quantile(df[,2], probs = 0.99))
    ) + geom_hline(yintercept = 4, lty = "dashed") +
    geom_vline(xintercept = 3, lty = "dashed")
  list(plot = p, name = name)
})

gridExtra::grid.arrange(three_samples[[1]]$plot, three_samples[[2]]$plot, 
                        three_samples[[3]]$plot, ncol = 3)

We want a TSS enrichment score of > 4 and a number of unique fragments > 1000 (log10(1000) = 3).

Plotting sample statistics

  • when we have distinct samples, it can be important to compare various metric between samples
  • ridge plots & violin plots are used for grouped data

p1 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) %>% 
  ggplot() +
  geom_density(aes(x = TSSEnrichment, fill = Sample), alpha = 0.8) 
## Warning: `as_data_frame()` was deprecated in tibble 2.0.0.
## Please use `as_tibble()` instead.
## The signature and semantics have changed, see `?as_tibble`.

p2 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) %>% 
  ggplot() +
  ggridges::geom_density_ridges(aes(x = TSSEnrichment, y = Sample,
                                    fill = Sample), alpha = 0.8) +
  theme(legend.position = "none")

p3 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) %>% 
  ggplot() +
  geom_violin(aes(x = Sample, y = TSSEnrichment, fill = Sample), alpha = 0.8) +
  geom_boxplot(aes(x = Sample, y = TSSEnrichment,fill = Sample), alpha = 0.4) + 
  theme(legend.position = "none") +
  labs(title = "TSS Enrichment")


cowplot::plot_grid(p3, p2, p1, ncol = 3)

p1 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>% 
  ggplot() +
  geom_density(aes(x = log10_nFrags, fill = Sample), alpha = 0.8)

p2 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>% 
  ggplot() +
  ggridges::geom_density_ridges(aes(x = log10_nFrags, y = Sample,
                                    fill = Sample), alpha = 0.8) +
  theme(legend.position = "none")

p3 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_"), log10_nFrags = log10(nFrags)) %>% 
  ggplot() +
  geom_violin(aes(x = Sample, y = log10_nFrags, fill = Sample), alpha = 0.8) +
  geom_boxplot(aes(x = Sample, y = log10_nFrags,fill = Sample), alpha = 0.4) + 
  theme(legend.position = "none") +
  labs(title = "number of fragments")


cowplot::plot_grid(p3, p2, p1, ncol = 3)

Plot Fragment Size Distribution & TSS Enrichment Profiles

  • the distribution of fragments size can be very different between samples, cell types and batches -> these differences do not necessarily correlate with differences in quality
  • the dip is the fragment size of a nucleosome ~147bp
  • TSS enrichment profiles
    • clear peak in the center
    • smaller shoulder peak right of the center caused by well positioned +1 nucleosome
p1 <- plotFragmentSizes(ArchRProj = proj)
p2 <- plotTSSEnrichment(ArchRProj = proj)
ggAlignPlots(p1, p2, type = "v")

Filtering Doublets

With the function addDoubleScores() information on predicted doublets has been added. Filter the putative doublets. They are not removed physically, but excluded from downstream analysis. ArchR automatically prints the number of cells removed from each sample and the corresponding percentage which is very handy.

arguments:

  • cutEnrich = minimum cutoff for DoubletEnrichment, number of simulated doublets divided by expected number given a random uniform distribution
  • cutScore = minimum cutoff for Doublet Score, represents -log10(binomial adjusted p-value) for the DoubletEnrichmentadd
  • filterRatio = maximum ratio of predicted doublets to filter based on number of pass-filter cells (A higher filterRatio means that more cells are removed) e.g. 5000 cells

maximum would be filterRatio * 5000 / 100000 = filterRatio * 5000 * 0.05

This way samples with different percentage of doublets will be filtered accordingly.

# in our case we now have 10 251 cells as opposed to 10 661 cells before
# filtering -> 410 cells were removed (3.85%)
proj <- filterDoublets(ArchRProj = proj)

Dimensionality reduction & Clustering

  • two other algorithms:
    • latent semantic indexing (LSI) in Signac
    • landmark diffusion maps (LDM) in SnapATAC
  • ArchR: optimized iterative LSI method
    • exhibits less susceptibility to batch effects
    • focuses on most variable features
    1. create a LSI Reduction from a subset of the total cells
    2. linearly project the remaining cells into this subspace with LSI projection (based on SVD)

Because we can have maximally two accessible alleles per cell, the scATAC-seq data is sparse. Therefore, the majority of accessible regions are not transposed, meaning that most loci will have 0 accessible alleles. A zero could mean “non-accessible” or “not sampled”. For many analysis we can use a binarized matix. Imporantly, the 1s have information, BUT the 0s do not!

A PCA would result in high inter-cell similarity at all 0 positions. An alternative approach for dimensionality reduction is a layered dimensionality reduction. First, Latent Semantic Indexing (LSI) is used. LSI is an approach from language processing. Different samples are the “documents” and different regions/peaks are the “words”.

Iterative LSI

  1. compute term frequency (depth normalization to 10,000 per single cell)

\(TF = \frac{C_{ij}}{F_{j}}\) with \(C_{ij}\) being the total number of counts for peak i in cell j and \(F_{j}\) being the total number of counts in cell j.

  1. Inverse document frequency
  • weights features by how often they occur
  • more weight to less frequent peaks

\(IDF = \frac{N}{n_{p}}\) with N being the total number of cells in the dataset and \(n_{p}\) being the total number of coutns for peak i across all cells.

  1. The term frequency TF is normalized by the inverse document frequncy IDF. You get a TF-IDF matrix (term frequency-inverse document frequency) which tells us how important a region/peak is to a sample. In other words you transform a binary matrix to a non-binary matrix.

\(TF-IDF = \log{1 + (TF * IDF) 10^{4}}\)

  1. SVD identifies the most valuable information across samples. Then we can use these most valuable features to represent the data in a lower dimensional space
  2. Clusters are identified with Seurat’s Shared Nearest Neighbor clustering
  3. Sum accessibility across all single cells in each cluster -> log-normalize
  4. Identify most variable features across the clusters
  5. repeat with most variable peaks as features

With LSI we can reduce the dimensionality of the sparse insertion matrix to tens or hundreds. Then UMAP or t-SNE can be used to visualize the data

Unlike in scRNA-seq we cannot select the top highly variable features before dimensionality reduction (high noise, low reproducibility). Rather the iterative LSI approach first computes a LSI on the most accessible tiles (this will identify clusters corresponding to the major cell types). Then, ArchR computes the average accessibility across these clusters across all features. Next, the most variable peaks across these clusters are identified. The most highly accessible peaks are the features of a new round of LSI. We can set how many rounds of LSI we want to be peformed.

Using iterative LSI reduces batch effects. If you see some batch effects you could try to add more LSI iterations and start from a lower initial clustering resolution. Also, the number of variable features can be lowered.

Estimated LSI

  • for extremely large scATAC-seq datasets
  1. subset of landmark cells (randomly selected) are used for LSI
  2. remaining cells are TF-IDF normlaized using the IDF determined from the landmark cells from step one
  3. normalized cells are porjected into the SVD space defined by the landmark cells

The LSI transformation is, therefore, based on a subset of cells only and the remaining cells are projected into this landmark LSI. This approach minimizes memory usage. The landmark set size depends on cell type proportions.

  • iterative LSI dimensionality reduction -> explore the parameters!

Batch correct with Harmony

  • dimensionality reduction object from ArchR isp passed to Harmony
proj <- addIterativeLSI(ArchRProj = proj, useMatrix = "TileMatrix", name = "IterativeLSI")
proj <- addIterativeLSI(
    ArchRProj = proj,
    useMatrix = "TileMatrix", 
    name = "IterativeLSI", 
    iterations = 2, 
    clusterParams = list( #See Seurat::FindClusters
        resolution = c(0.2), 
        sampleCells = 10000, 
        n.start = 10
    ), 
    varFeatures = 25000, 
    dimsToUse = 1:30
)

Clustering

Calling clusters in this new space uses the Seurat’s graph clustering function as default clustering method. The Seurat method first computs KNN graph and then a modularity optimization technique to cluster the cells (iteratively group cells together with Louvian algorithm using 10 random starts). Another option is to use “Scran”. The default number of nearest neighbors used is 10. The minimum number of cells for a cluster to be called a cluster is set to 5 by default. The maximum number of clusters to be called is set to 25 by default.

proj <- addClusters(input = proj, reducedDims = "IterativeLSI")
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 10250
## Number of edges: 473760
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8640
## Number of communities: 13
## Elapsed time: 1 seconds

Visualizing a UMAP embedding

  • uses uwot package
  • various attributes of the data can be visualized
    • these are stored in a matrix called cellColData
    • which variable the plot is colored by is specified by colorBy and name parameter
proj <- addUMAP(ArchRProj = proj, reducedDims = "IterativeLSI")
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid uid value replaced by that for user 'nobody''
## Warning: invalid uid value replaced by that for user 'nobody'
## Warning in sprintf(gettext(fmt, domain = domain), ...): one argument not used by
## format 'invalid gid value replaced by that for user 'nobody''
## Warning: invalid gid value replaced by that for user 'nobody'
df <- as_data_frame(cbind(getCellColData(proj), getEmbedding(proj)) ) %>%
  rename(c(umap1 = IterativeLSI.UMAP_Dimension_1, umap2  = IterativeLSI.UMAP_Dimension_2))

variables <- c("Clusters", "Sample", "nFrags", "DoubletScore")

plots1 <- map(c("Clusters", "Sample"), function(n){
  ggplot() +
  geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"), 
                 col = df %>% pull(n)), size = .04) +
    guides(col=guide_legend(title=paste0(n))) +
    xlab("umap1") +
    ylab("umpa2") +
    labs(title = paste0(n))
})

plots2 <- map(c("nFrags", "DoubletScore"), function(n){
  ggplot() +
  geom_point(aes(x = df %>% pull("umap1"), y = df %>% pull("umap2"), 
                 col = df %>% pull(n)), size = .04) +
    scale_color_viridis_c() +
    guides(fill=guide_legend(title=paste0(n))) +
    xlab("umap1") +
    ylab("umpa2") +
    labs(title = paste0(n))
})

do.call(gridExtra::grid.arrange, c(plots1, ncol=2))#, nrow = 2))

```#{r, fig.width=8} # color by sample p1 <- plotEmbedding(ArchRProj = proj, colorBy = “cellColData”, name = “Sample”, embedding = “UMAP”)

color by cluster

p2 <- plotEmbedding(ArchRProj = proj, colorBy = “cellColData”, name = “Clusters”, embedding = “UMAP”)

ggAlignPlots(p1, p2, type = “h”)


# Cluster assignment using gene scores

For the toy dataset marker genes of known hematopoietic regulators
can be used. Using MAGIC we add imputation weights to smooth the dropout noise in the gene scores


```r
proj <- addImputeWeights(proj)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes  <- c(
    "CD34",  #Early Progenitor
    "GATA1", #Erythroid
    "PAX5", "MS4A1", "MME", #B-Cell Trajectory
    "CD14", "MPO", #Monocytes
    "CD3D", "CD8A"#TCells
  )
p <- plotEmbedding(
    ArchRProj = proj, 
    colorBy = "GeneScoreMatrix", 
    name = markerGenes, 
    embedding = "UMAP",
    imputeWeights = getImputeWeights(proj)
)
do.call(gridExtra::grid.arrange, c(p, ncol = 3))

Visualizing Genome Browser Tracks

Browse local chromatin accessibility at marker genes. Plot genome browser tracks per cluster

p <- plotBrowserTrack(
    ArchRProj = proj, 
    groupBy = "Clusters", 
    geneSymbol = markerGenes, 
    upstream = 50000,
    downstream = 50000
)
## GRanges object with 9 ranges and 2 metadata columns:
##       seqnames              ranges strand |     gene_id      symbol
##          <Rle>           <IRanges>  <Rle> | <character> <character>
##   [1]     chr1 208059883-208084683      - |         947        CD34
##   [2]     chrX   48644982-48652717      + |        2623       GATA1
##   [3]     chr9   36838531-37034476      - |        5079        PAX5
##   [4]    chr11   60223282-60238225      + |         931       MS4A1
##   [5]     chr3 154741913-154901518      + |        4311         MME
##   [6]     chr5 140011313-140013286      - |         929        CD14
##   [7]    chr17   56347217-56358296      - |        4353         MPO
##   [8]    chr11 118209789-118213459      - |         915        CD3D
##   [9]     chr2   87011728-87035519      - |         925        CD8A
##   -------
##   seqinfo: 24 sequences from hg19 genome
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
grid::grid.newpage()
grid::grid.draw(p$CD14)

Integration with scRNA-seq

  • the scATAC-seq gene score matrix is compared with the scRNA-seq gene expression matrix
  • for this alignment the FindTransferAnchors() function from Seurat is used
  • to scale for large sample size, this process is parallelized
  • for each cell in ATAC we find the cell in scRNA-seq that looks most similar -> assign the correpsonding gene expression to that cell.

Apart from using this information for identifying clusters we can also use it for identifying predicted cis-regulatory elements.

if(!file.exists("scRNA-Hematopoiesis-Granja-2019.rds")){
    download.file(
        url = "https://jeffgranja.s3.amazonaws.com/ArchR/TestData/scRNA-Hematopoiesis-Granja-2019.rds",
        destfile = "scRNA-Hematopoiesis-Granja-2019.rds"
    )
}

# ranged summarized Experiment
seRNA <- readRDS("scRNA-Hematopoiesis-Granja-2019.rds")
seRNA
## class: RangedSummarizedExperiment 
## dim: 20287 35582 
## metadata(0):
## assays(1): counts
## rownames(20287): FAM138A OR4F5 ... S100B PRMT2
## rowData names(3): gene_name gene_id exonLength
## colnames(35582): CD34_32_R5:AAACCTGAGTATCGAA-1
##   CD34_32_R5:AAACCTGAGTCGTTTG-1 ...
##   BMMC_10x_GREENLEAF_REP2:TTTGTTGCATGTGTCA-1
##   BMMC_10x_GREENLEAF_REP2:TTTGTTGCATTGAAAG-1
## colData names(10): Group nUMI_pre ... BioClassification Barcode

Lets have a look at the count matrix:

# sparse count matrix of scRNA-seq
assays(seRNA)[[1]][1:10, 1:5]
## 10 x 5 sparse Matrix of class "dgCMatrix"
##            CD34_32_R5:AAACCTGAGTATCGAA-1 CD34_32_R5:AAACCTGAGTCGTTTG-1
## FAM138A                                .                             .
## OR4F5                                  .                             .
## AL627309.1                             .                             .
## OR4F29                                 .                             .
## OR4F16                                 .                             .
## FAM87B                                 .                             .
## LINC00115                              .                             .
## FAM41C                                 1                             .
## AL645608.2                             .                             .
## SAMD11                                 .                             .
##            CD34_32_R5:AAACCTGGTTCCACAA-1 CD34_32_R5:AAACGGGAGCTTCGCG-1
## FAM138A                                .                             .
## OR4F5                                  .                             .
## AL627309.1                             .                             .
## OR4F29                                 .                             .
## OR4F16                                 .                             .
## FAM87B                                 .                             .
## LINC00115                              .                             .
## FAM41C                                 1                             .
## AL645608.2                             .                             .
## SAMD11                                 .                             .
##            CD34_32_R5:AAACGGGAGGGAGTAA-1
## FAM138A                                .
## OR4F5                                  .
## AL627309.1                             .
## OR4F29                                 .
## OR4F16                                 .
## FAM87B                                 .
## LINC00115                              .
## FAM41C                                 .
## AL645608.2                             .
## SAMD11                                 .

Metadata of the scRNA-seq dataset:

We already have clustering, umap embeddings and cell types.

colData(seRNA) %>% head %>% knitr::kable()
Group nUMI_pre nUMI nGene initialClusters UMAP1 UMAP2 Clusters BioClassification Barcode
CD34_32_R5:AAACCTGAGTATCGAA-1 CD34_D2T1 17876 8303 3187 Cluster1 -6.113410 4.616498 Cluster5 05_CMP.LMPP AAACCTGAGTATCGAA-1
CD34_32_R5:AAACCTGAGTCGTTTG-1 CD34_D2T1 9277 3917 1787 Cluster2 -8.800932 -1.228907 Cluster8 08_GMP.Neut AAACCTGAGTCGTTTG-1
CD34_32_R5:AAACCTGGTTCCACAA-1 CD34_D2T1 13073 6023 2552 Cluster3 -9.723482 7.335178 Cluster1 01_HSC AAACCTGGTTCCACAA-1
CD34_32_R5:AAACGGGAGCTTCGCG-1 CD34_D2T1 8412 4493 2191 Cluster4 -4.293071 5.692705 Cluster6 06_CLP.1 AAACGGGAGCTTCGCG-1
CD34_32_R5:AAACGGGAGGGAGTAA-1 CD34_D2T1 11914 5190 2322 Cluster3 -7.989706 9.108693 Cluster1 01_HSC AAACGGGAGGGAGTAA-1
CD34_32_R5:AAACGGGAGTTACGGG-1 CD34_D2T1 12075 4634 2152 Cluster3 -7.271959 6.980415 Cluster1 01_HSC AAACGGGAGTTACGGG-1

Plot Quality Metrics of the scRNA-seq dataset:

as_data_frame(colData(seRNA)) %>% 
  select(nUMI, nGene, Group) %>% 
  pivot_longer(cols = !Group, names_to = "stat") %>% 
  ggplot() +
  geom_violin(aes(x = Group, y = value, fill = Group)) +
  facet_wrap(~stat, scales = "free") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        legend.position = "none") +
  xlab("Sample")

  #pivot_longer(cols = !s)
df <- as_data_frame(colData(seRNA))

p1 <- ggplot() +
geom_point(aes(x = df %>% pull("UMAP1"), y = df %>% pull("UMAP2"), 
               col = df %>% pull("BioClassification")), size = .04) +
  guides(col=guide_legend(title="CellType")) +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "scRNA-seq dataset - cell type")

p2 <- ggplot() +
geom_point(aes(x = df %>% pull("UMAP1"), y = df %>% pull("UMAP2"), 
               col = df %>% pull("nGene")), size = .04) +
  guides(col=guide_legend(title="number of genes")) +
  scale_color_viridis_c() +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "scRNA-seq dataset - gene number")

p3 <- df %>% ggplot() +
  geom_point(aes(x = UMAP1, y = UMAP2, col = Group), size = .04) +
  guides(col=guide_legend(title="Sample")) +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "scRNA-seq dataset - sample")

p1

gridExtra::grid.arrange(p2, p3, ncol = 2)

table(colData(seRNA)$BioClassification)
## 
##         01_HSC 02_Early.Eryth  03_Late.Eryth  04_Early.Baso    05_CMP.LMPP 
##           1425           1653            446            111           2260 
##       06_CLP.1         07_GMP    08_GMP.Neut         09_pDC         10_cDC 
##            903           2097           1050            544            325 
## 11_CD14.Mono.1 12_CD14.Mono.2   13_CD16.Mono         14_Unk       15_CLP.2 
##           1800           4222            292            520            377 
##       16_Pre.B           17_B      18_Plasma       19_CD8.N      20_CD4.N1 
##            710           1711             62           1521           2470 
##      21_CD4.N2       22_CD4.M      23_CD8.EM      24_CD8.CM          25_NK 
##           2364           3539            796           2080           2143 
##         26_Unk 
##            161

Cells from both modalities

p1 <- as_data_frame(getCellColData(proj)) %>% 
  mutate(Sample = str_remove(Sample, "scATAC_")) 
  
  
atac <- as_data_frame(getCellColData(proj)) %>% 
  rownames_to_column("cell") %>% 
  mutate(cell = str_extract(rownames(getCellColData(proj)), "(?<=#)[^#]+"))
atac
rna <- as_data_frame(colData(seRNA)) %>% 
  rownames_to_column("cell") %>%
  mutate(cell = str_extract(rownames(colData(seRNA)), "(?<=:)[^:]+")) %>% 
  filter(cell %in% atac[["cell"]])

cbind(atac["nFrags"], rna["nUMI"])
  

Unconstrained & Constrained integration

Unconstrained integration:

  • takes all cells of scATAC-seq data and attempts to align them to any of the scRNA-seq cells
  • this provides preliminary cluster identities which will serve as prior knowledge for the constrained integration
  • the quality can be improved by constraining the integration

The integration matrix will be stored in the ArchR project via matrixName. The other parameters will be saved in the cellColData (nameCell = store matched cell ID from scRNA-seq, nameGroup = store group ID from scRNA-seq, nameScore = store cross-platform integration score)

proj <- addGeneIntegrationMatrix(
    ArchRProj = proj, 
    useMatrix = "GeneScoreMatrix",
    matrixName = "GeneIntegrationMatrix",
    reducedDims = "IterativeLSI",
    seRNA = seRNA,
    addToArrow = FALSE,
    groupRNA = "BioClassification", # Bioclassification is a column in
    # the colData of the seRNA object, this column will be used to 
    # determine the subgroupings specified in groupList for constrained integration
    # it is also used for the nameGroup output of this function
    nameCell = "predictedCell_Un", # name the cellColData for the 
    # predicted scRNA-seq cell in the specified ArchRProject ->
    # will add a column to the project
    nameGroup = "predictedGroup_Un",
    nameScore = "predictedScore_Un"
)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
df <- as_data_frame(cbind(getCellColData(proj), getEmbedding(proj)) ) %>%
  rename(c(umap1 = IterativeLSI.UMAP_Dimension_1, umap2  = IterativeLSI.UMAP_Dimension_2))

p1 <- df %>% ggplot() +
  geom_point(aes(x = umap1, y = umap2, col = predictedGroup_Un), size = .04) +
  guides(col=guide_legend(title="predicted cell type")) +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "Predicted cell types after unconstrained integration")

p2 <- df %>% ggplot() +
  geom_point(aes(x = umap1, y = umap2, col = predictedScore_Un), size = .04) +
  guides(col=guide_legend(title="prediction score")) +
  scale_color_viridis_c() +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "Predicted cell types after unconstrained integration")

gridExtra::grid.arrange(p1, p2, ncol = 1)

Constrained integration:

  • prior knowledge of the cell types is used to limit the search space of the alignment -> e.g. if we know that clusters A, B, C in scATAC-seq data correspond to 3 different T cell clusters and Clusters X, Y in scRNA-seq data correspond to two T cell clusters, we could try to specifically align these cells
  1. Identify which cell types from scRNA-seq data are most abundant in scATAC-seq clusters?
  2. confusion matrix is created that looks at the intersection of Clusters and predicted group which contains the cell types as identfied by scRNA-seq
  3. This shows which scRNA-seq cell type is most abundant in each of the scATAC clusters
# in the confusion matrix the rows correspond to clusters in scATAC data
# the columns correspond to cell types in scRNA-seq data
cM <- as.matrix(confusionMatrix(proj$Clusters, proj$predictedGroup_Un))
cM[, 1:5]
##     17_B 16_Pre.B 08_GMP.Neut 25_NK 11_CD14.Mono.1
## C4   407        1           0     1              0
## C2     3      330           0     2              0
## C10    3        0         276     0             74
## C6     0        0           0   570              0
## C8     0        0           6     0           1237
## C12    0        0          26     5              0
## C5     0        0           0     4              0
## C11    0        0         751     0              2
## C7     0        0           0    14              0
## C13    0        0          60     2              0
## C1     0        0           0     0              0
## C9     2        0           3     0            366
## C3     0        1           1     0              0

preClust <- colnames(cM)[apply(cM, 1 , which.max)]
cbind(preClust, rownames(cM)) #Assignments
##       preClust              
##  [1,] "17_B"           "C4" 
##  [2,] "16_Pre.B"       "C2" 
##  [3,] "08_GMP.Neut"    "C10"
##  [4,] "25_NK"          "C6" 
##  [5,] "11_CD14.Mono.1" "C8" 
##  [6,] "02_Early.Eryth" "C12"
##  [7,] "21_CD4.N2"      "C5" 
##  [8,] "08_GMP.Neut"    "C11"
##  [9,] "22_CD4.M"       "C7" 
## [10,] "01_HSC"         "C13"
## [11,] "09_pDC"         "C1" 
## [12,] "12_CD14.Mono.2" "C9" 
## [13,] "15_CLP.2"       "C3"

Which cells in the scRNA-seq correspond to Tcells and NK cells?

Use pattern matching strings in combination with grep to extract the scATAC-seq clusters that correspond to these scRNA-seq cell types. | acts as an or statement, so we search for any row in the preClust column of the confusion matix that matches a scRNA-seq cluster number.

unique(proj$predictedGroup_Un)
##  [1] "17_B"           "16_Pre.B"       "08_GMP.Neut"    "25_NK"         
##  [5] "11_CD14.Mono.1" "07_GMP"         "02_Early.Eryth" "24_CD8.CM"     
##  [9] "05_CMP.LMPP"    "03_Late.Eryth"  "22_CD4.M"       "09_pDC"        
## [13] "21_CD4.N2"      "15_CLP.2"       "01_HSC"         "19_CD8.N"      
## [17] "12_CD14.Mono.2" "20_CD4.N1"      "06_CLP.1"       "04_Early.Baso" 
## [21] "26_Unk"         "23_CD8.EM"      "13_CD16.Mono"
cTNK <- paste0(paste0(19:25), collapse = "|")
cTNK
## [1] "19|20|21|22|23|24|25"

cNonTNK <- paste0(c(paste0("0", 1:9), 10:13, 15:18), collapse = "|")
cNonTNK
## [1] "01|02|03|04|05|06|07|08|09|10|11|12|13|15|16|17|18"
clustTNK <- rownames(cM)[grep(cTNK, preClust)]
clustTNK
## [1] "C6" "C5" "C7"

clustNonTNK <- rownames(cM)[grep(cNonTNK, preClust)]
clustNonTNK
##  [1] "C4"  "C2"  "C10" "C8"  "C12" "C11" "C13" "C1"  "C9"  "C3"

Identify scRNA-seq cells corresponding to the same cell types.

#RNA get cells in these categories
rnaTNK <- colnames(seRNA)[grep(cTNK, colData(seRNA)$BioClassification)]
head(rnaTNK)
## [1] "PBMC_10x_GREENLEAF_REP1:AAACCCAGTCGTCATA-1"
## [2] "PBMC_10x_GREENLEAF_REP1:AAACCCATCCGATGTA-1"
## [3] "PBMC_10x_GREENLEAF_REP1:AAACCCATCTCAACGA-1"
## [4] "PBMC_10x_GREENLEAF_REP1:AAACCCATCTCTCGAC-1"
## [5] "PBMC_10x_GREENLEAF_REP1:AAACGAACAATCGTCA-1"
## [6] "PBMC_10x_GREENLEAF_REP1:AAACGAACACGATTCA-1"

# get cell not in these categories
rnaNonTNK <- colnames(seRNA)[grep(cNonTNK, colData(seRNA)$BioClassification)]
head(rnaNonTNK)
## [1] "CD34_32_R5:AAACCTGAGTATCGAA-1" "CD34_32_R5:AAACCTGAGTCGTTTG-1"
## [3] "CD34_32_R5:AAACCTGGTTCCACAA-1" "CD34_32_R5:AAACGGGAGCTTCGCG-1"
## [5] "CD34_32_R5:AAACGGGAGGGAGTAA-1" "CD34_32_R5:AAACGGGAGTTACGGG-1"

We create a nested list with two vectors of cell IDs

groupList <- SimpleList(
    TNK = SimpleList(
        ATAC = proj$cellNames[proj$Clusters %in% clustTNK],
        RNA = rnaTNK
    ),
    NonTNK = SimpleList(
        ATAC = proj$cellNames[proj$Clusters %in% clustNonTNK],
        RNA = rnaNonTNK
    )    
)

groupList[[1]][[1]] %>% head
## [1] "scATAC_BMMC_R1#GTAGGAGCATTATGGC-1" "scATAC_BMMC_R1#CTAGGATTCTATGAGC-1"
## [3] "scATAC_BMMC_R1#AGTTTGGCACGCGCAT-1" "scATAC_BMMC_R1#GCAGATTGTATGGGTG-1"
## [5] "scATAC_BMMC_R1#ACATGGTCAGTATCTG-1" "scATAC_BMMC_R1#AGATTCGCATAGTCCA-1"
proj <- addGeneIntegrationMatrix(
    ArchRProj = proj, 
    useMatrix = "GeneScoreMatrix",
    matrixName = "GeneIntegrationMatrix",
    reducedDims = "IterativeLSI",
    seRNA = seRNA,
    addToArrow = FALSE, 
    groupList = groupList,
    groupRNA = "BioClassification",
    nameCell = "predictedCell_Co",
    nameGroup = "predictedGroup_Co",
    nameScore = "predictedScore_Co"
)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'

## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
pal <- paletteDiscrete(values = colData(seRNA)$BioClassification)
pal
##         01_HSC 02_Early.Eryth  03_Late.Eryth  04_Early.Baso    05_CMP.LMPP 
##      "#D51F26"      "#502A59"      "#235D55"      "#3D6E57"      "#8D2B8B" 
##       06_CLP.1         07_GMP    08_GMP.Neut         09_pDC         10_cDC 
##      "#DE6C3E"      "#F9B712"      "#D8CE42"      "#8E9ACD"      "#B774B1" 
## 11_CD14.Mono.1 12_CD14.Mono.2   13_CD16.Mono         14_Unk       15_CLP.2 
##      "#D69FC8"      "#C7C8DE"      "#8FD3D4"      "#89C86E"      "#CC9672" 
##       16_Pre.B           17_B      18_Plasma       19_CD8.N      20_CD4.N1 
##      "#CF7E96"      "#A27AA4"      "#CD4F32"      "#6B977E"      "#518AA3" 
##      21_CD4.N2       22_CD4.M      23_CD8.EM      24_CD8.CM          25_NK 
##      "#5A5297"      "#0F707D"      "#5E2E32"      "#A95A3C"      "#B28D5C" 
##         26_Unk 
##      "#3D3D3D"

Compare unconstrained and constrained integration

Plot the integration for unconstrained (left) and constrained (right) integration. We overlay the scRNA-seq cell types on the ATAC-seq data based on the unconstrained/constrained integration. In the plot below the difference is very subtle, because the cell types of interest are very distinct. There are subtle differences, for example in the T cell clusters. Could we also try this approach with constraining other cell types? Can we combine different constrains/groups?

df <- as_data_frame(cbind(getCellColData(proj), getEmbedding(proj)) ) %>%
  rename(c(umap1 = IterativeLSI.UMAP_Dimension_1, umap2  = IterativeLSI.UMAP_Dimension_2))

p1 <- df %>% ggplot() +
  geom_point(aes(x = umap1, y = umap2, col = predictedGroup_Un), size = .04) +
  guides(col=guide_legend(title="predicted cell type")) +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "Predicted cell types after unconstrained integration")

p2 <- df %>% ggplot() +
  geom_point(aes(x = umap1, y = umap2, col = predictedScore_Un), size = .04) +
  guides(col=guide_legend(title="prediction score")) +
  scale_color_viridis_c() +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "Predicted cell types after unconstrained integration")

p3 <- df %>% ggplot() +
  geom_point(aes(x = umap1, y = umap2, col = predictedGroup_Co), size = .04) +
  guides(col=guide_legend(title="predicted cell type")) +
  #geom_label(predictedGroup_Co) +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "Predicted cell types after unconstrained integration")

p4<- df %>% ggplot() +
  geom_point(aes(x = umap1, y = umap2, col = predictedScore_Co), size = .04) +
  guides(col=guide_legend(title="prediction score")) +
  scale_color_viridis_c() +
  xlab("umap1") +
  ylab("umpa2") +
  labs(title = "Predicted cell types after constrained integration")

gridExtra::grid.arrange(p1, p3, ncol = 1)

p1 <- plotEmbedding(
    proj, 
    colorBy = "cellColData", 
    name = "predictedGroup_Un", 
    pal = pal
)


p2 <- plotEmbedding(
    proj, 
    colorBy = "cellColData", 
    name = "predictedGroup_Co", 
    pal = pal
)


ggAlignPlots(p1, p2, type = "h")

Adding pseudo-scRNA-seq profiles for each ATAC-seq cell

If we are happy with the integration results we add them to the Arrow Files. Then we can compare the linked gene expression with the inferred gene expression obtained through the gene scores.

#~5 minutes
proj <- addGeneIntegrationMatrix(
  ArchRProj = proj, 
  useMatrix = "GeneScoreMatrix",
  matrixName = "GeneIntegrationMatrix",
  reducedDims = "IterativeLSI",
  seRNA = seRNA,
  addToArrow = TRUE,
  force= TRUE,
  groupList = groupList,
  groupRNA = "BioClassification",
  nameCell = "predictedCell",
  nameGroup = "predictedGroup",
  nameScore = "predictedScore",
  
)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'

## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'


getAvailableMatrices(proj)
## [1] "GeneIntegrationMatrix" "GeneScoreMatrix"       "TileMatrix"

proj <- addImputeWeights(proj)
## Warning in sprintf("Completed Getting Magic Weights!",
## round(object.size(weightList)/10^9, : one argument not used by format 'Completed
## Getting Magic Weights!'
markerGenes  <- c(
  "CD34", #Early Progenitor
  "GATA1", #Erythroid
  "PAX5", "MS4A1", #B-Cell Trajectory
  "CD14", #Monocytes
  "CD3D", "CD8A", "TBX21", "IL7R" #TCells
)

# gene expression values from GeneIntegrationMatrix
p1 <- plotEmbedding(
  ArchRProj = proj, 
  colorBy = "GeneIntegrationMatrix", 
  name = markerGenes, 
  continuousSet = "horizonExtra",
  embedding = "UMAP",
  imputeWeights = getImputeWeights(proj)
)


# Gene score values from GeneScoreMatrix

p2 <- plotEmbedding(
  ArchRProj = proj, 
  colorBy = "GeneScoreMatrix", 
  continuousSet = "horizonExtra",
  name = markerGenes, 
  embedding = "UMAP",
  imputeWeights = getImputeWeights(proj)
)

In the plot below you can see the gene expression values from integration with the scRNA-seq data.

p1c <- lapply(p1, function(x){
  x + guides(color = FALSE, fill = FALSE) + 
    theme_ArchR(baseSize = 6.5) +
    theme(plot.margin = unit(c(0, 0, 0, 0), "cm")) +
    theme(
      axis.text.x=element_blank(), 
      axis.ticks.x=element_blank(), 
      axis.text.y=element_blank(), 
      axis.ticks.y=element_blank()
    ) 
})
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

p2c <- lapply(p2, function(x){
  x + guides(color = FALSE, fill = FALSE) + 
    theme_ArchR(baseSize = 6.5) +
    theme(plot.margin = unit(c(0, 0, 0, 0), "cm")) +
    theme(
      axis.text.x=element_blank(), 
      axis.ticks.x=element_blank(), 
      axis.text.y=element_blank(), 
      axis.ticks.y=element_blank()
    ) 
})
## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.

## Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
## "none")` instead.
do.call(cowplot::plot_grid, c(list(ncol = 3), p1c))

do.call(cowplot::plot_grid, c(list(ncol = 3), p2c))

In the plot below you can see the gene score values which we calculate above. When comparing these values with the gene expression from above we can see that the gene scores calculated from scATAC-seq are more sensitive in comparison. This means that some regions are open (primed), but not yet transcribed. This is an information that you can only get with multi-omics data.

do.call(cowplot::plot_grid, c(list(ncol = 3), p2c))

The results from the two different methdos for inferring gene expression are similar, but not identical.

cm <- confusionMatrix(proj$Clusters, proj$predictedGroup)
cm[1:10, 1:5]
## 10 x 5 sparse Matrix of class "dgCMatrix"
##     08_GMP.Neut 16_Pre.B 25_NK 07_GMP 04_Early.Baso
## C4            1        1     .      1             .
## C2            .      350     .      .             .
## C10         201        .     .    241             1
## C6            .        .   425      .             .
## C8            3        .     .    145             3
## C12           3        .     .      .           151
## C5            .        .     4      .             .
## C11         276        .     .    133            51
## C7            .        .     2      .             .
## C13          12        .     .     18             7
dim(cm)
## [1] 13 23
# for each row get the index of the maximum = predicted
# group which best defines a clsuter
apply(cm, 1, which.max)
##  C4  C2 C10  C6  C8 C12  C5 C11  C7 C13  C1  C9  C3 
##   9   2   4   3  13   8   6   7  11  19  12  10  20

# using these indices extract the corresponding predicted cell type
# we will use these predicted cell types as our new cluster labels
labelNew <- colnames(cm)[apply(cm, 1, which.max)]
labelNew
##  [1] "17_B"           "16_Pre.B"       "07_GMP"         "25_NK"         
##  [5] "11_CD14.Mono.1" "03_Late.Eryth"  "20_CD4.N1"      "05_CMP.LMPP"   
##  [9] "22_CD4.M"       "01_HSC"         "09_pDC"         "12_CD14.Mono.2"
## [13] "15_CLP.2"

Rename the scRNA-seq cluster labels to something more interpretable:

remapClust <- c(
    "01_HSC" = "Progenitor",
    "02_Early.Eryth" = "Erythroid",
    "03_Late.Eryth" = "Erythroid",
    "04_Early.Baso" = "Basophil",
    "05_CMP.LMPP" = "Progenitor",
    "06_CLP.1" = "CLP",
    "07_GMP" = "GMP",
    "08_GMP.Neut" = "GMP",
    "09_pDC" = "pDC",
    "10_cDC" = "cDC",
    "11_CD14.Mono.1" = "Mono",
    "12_CD14.Mono.2" = "Mono",
    "13_CD16.Mono" = "Mono",
    "15_CLP.2" = "CLP",
    "16_Pre.B" = "PreB",
    "17_B" = "B",
    "18_Plasma" = "Plasma",
    "19_CD8.N" = "CD8.N",
    "20_CD4.N1" = "CD4.N",
    "21_CD4.N2" = "CD4.N",
    "22_CD4.M" = "CD4.M",
    "23_CD8.EM" = "CD8.EM",
    "24_CD8.CM" = "CD8.CM",
    "25_NK" = "NK"
)
remapClust <- remapClust[names(remapClust) %in% labelNew]
remapClust
##         01_HSC  03_Late.Eryth    05_CMP.LMPP         07_GMP         09_pDC 
##   "Progenitor"    "Erythroid"   "Progenitor"          "GMP"          "pDC" 
## 11_CD14.Mono.1 12_CD14.Mono.2       15_CLP.2       16_Pre.B           17_B 
##         "Mono"         "Mono"          "CLP"         "PreB"            "B" 
##      20_CD4.N1       22_CD4.M          25_NK 
##        "CD4.N"        "CD4.M"           "NK"

Using mapLabels() you can convert the labels to the new system

labelNew2 <- mapLabels(labelNew, oldLabels = names(remapClust), newLabels = remapClust)
labelNew2
##  [1] "B"          "PreB"       "GMP"        "NK"         "Mono"      
##  [6] "Erythroid"  "CD4.N"      "Progenitor" "CD4.M"      "Progenitor"
## [11] "pDC"        "Mono"       "CLP"
proj$Clusters2 <- mapLabels(proj$Clusters, newLabels = labelNew2, oldLabels = unique(proj$Clusters))
p1 <- plotEmbedding(proj, colorBy = "cellColData", name = "Clusters2")
p1

All of the above analysis is also a great starting point for more complex gene regulation analysis.

saveArchRProject(proj, outputDirectory = "Save_Vignette_object_1", load = FALSE)
LS0tCnRpdGxlOiAiQXJjaFIgRmlyc3Qgc3RlcHMiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiA1CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlCi0tLQoKCjxzdHlsZT4KYm9keSB7CnRleHQtYWxpZ246IGp1c3RpZnl9Cjwvc3R5bGU+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGNhY2hlID0gRkFMU0UsIGF1dG9kZXAgPSBUUlVFLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9ICIvb21pY3MvZ3JvdXBzL09FMDUzMy9pbnRlcm5hbC9rYXRoYXJpbmEvc2NEb1JJL0FyY2hSIikKc2V0d2QoIi9vbWljcy9ncm91cHMvT0UwNTMzL2ludGVybmFsL2thdGhhcmluYS9zY0RvUkkvQXJjaFIvIikKCnNldC5zZWVkKDEpCmBgYAoKCgpBcmNoUiB0YWtlcyBhcyBpbnB1dCBhbGlnbmVkIEJBTSBvciBmcmFnbWVudCBmaWxlcy4gVGhlc2UgZmlsZXMgYXJlIHN0b3JlZCBpbiB0aGUgCkhERjUgZmlsZSBmb3JtYXQgKGhpZXJhcmNoaWNhbCBkYXRhIGZvcm1hdCB2ZXJzaW9uNSkuIFRoZSBIREY1IGZpbGVzIGFyZSB0aGUgCmNvbnN0aXR1ZW50IHBpZWNlcyBvZiBhbiBBcmNoUiBhbmFseXNpcy4gVGhleSBhcmUgY2FsbGVkIEFycm93IGZpbGVzLiBBbGwgQXJyb3cKZmlsZXMgYXJlIGdyb3VwZWQgaW50byBhIHByb2plY3QsIGEgY29tcHJlc3NlZCBSIGRhdGEgZmlsZS4gVGhlIEZpbGVzIGFyZSBhY2Nlc3NlZCAKaW4gbWluaW1hbCBjaHVua3MgKHBhcmFsbGVsIHJlYWQgYW5kIHdyaXRlIG9wZXJhdGlvbnMpLiBUaGVyZWZvcmUsIGluIG1lbW9yeSB3ZQpkbyBub3QgaGF2ZSBhbnkgbGFyZ2UgZmlsZSBzaXplcy4KClRvIHNlbGVjdCBoaWdoIHF1YWxpdHkgY2VsbHMgVFNTIGVucmljaG1lbnQgc2NvcmVzIGFyZSB1c2VkLiAKCgogIAojIEZpcnN0IHN0ZXBzIGluIEFyY2hSCiAgCiMjIExvYWQgbGlicmFyaWVzCiAgCmBgYHtyfQpsaWJyYXJ5KEFyY2hSKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHJoZGY1KQpsaWJyYXJ5KHV3b3QpCmxpYnJhcnkodGlkeXZlcnNlKQojbGlicmFyeShjYXJldCkKaDVkaXNhYmxlRmlsZUxvY2tpbmcoKQpgYGAKICAKICAKYGBge3J9CmlucHV0RmlsZXMgPC0gZ2V0VHV0b3JpYWxEYXRhKCJIZW1hdG9wb2llc2lzIikKYGBgCgoKYGBge3J9CmFkZEFyY2hSR2Vub21lKCJoZzE5IikKYGBgCgojIyBDcmVhdGUgQXJyb3cgRmlsZXMKCjEuIHJlYWQgYWNjZXNzaWJsZSBmcmFnbWVudHMgCjIuIGNhbGN1bGF0ZSBRQyBpbmZvcm1hdGlvbiBmb3IgZWFjaCBjZWxsIChUU1MgZW5yaWNobWVudCBzY29yZXMgYW5kIG51Y2Vsb3NvbWUgaW5mbykKMy4gZmlsdGVyIGNlbGxzIGJhc2VkIG9uIFFDIHBhcmFtZXRlcnMKNC4gY3JlYXRlIGdlbm9tZS13aWRlIHRpbGUgbWF0cml4IHVzaW5nIDUwMC1icCBiaW5zCjUuIGNyZWF0ZSBHZW5lU2NvcmVNYXRyaXggdXNpbmcgZ2VuZSBhbm5vdGF0aW9uCgoKYGBge3J9CkFycm93RmlsZXMgPC0gY3JlYXRlQXJyb3dGaWxlcygKICBpbnB1dEZpbGVzID0gaW5wdXRGaWxlcywKICBzYW1wbGVOYW1lcyA9IG5hbWVzKGlucHV0RmlsZXMpLAogIG1pblRTUyA9IDQsICNEb250IHNldCB0aGlzIHRvbyBoaWdoIGJlY2F1c2UgeW91IGNhbiBhbHdheXMgaW5jcmVhc2UgbGF0ZXIKICBtaW5GcmFncyA9IDEwMDAsICMgbWluaW11bSBudW1iZXIgb2YgbWFwcGVkIGZyYWdtZW50cyByZXF1aXJlZAogICMgY291bnQgbWF0cml4LCBpbnN0ZWFkIG9mIHVzaW5nIHBlYWsgaXQgdXNlcyBmaXhlZC13aWR0aCBzbGlkaW5nIHdpbmRvdyBvZiBiaW5zIGFjcm9zcyB0aGUgd2hvbGUgZ2Vub21lCiAgYWRkVGlsZU1hdCA9IFRSVUUsIAogIGFkZEdlbmVTY29yZU1hdCA9IFRSVUUsICMgdXNlcyBzaWduYWwgcHJveGltYWwgdG8gdGhlIFRTUyB0byBlc3RpbWF0ZSBnZW5lIGFjdGl2aXR5CiAgc3ViVGhyZWFkaW5nID0gRkFMU0UsCiAgbWF4RnJhZ3MgPSAxZSswNSwKICBtaW5GcmFnU2l6ZSA9IDEwLAogIG1heEZyYWdTaXplID0gMjAwMCwKICBRQ0RpciA9ICJRdWFsaXR5Q29udHJvbCIsCiAgIyB0aGUgbGVuZ3RoIGluIGJwIHRoYXQgd3JhcHMgYXJvdW5kIG51Y2xlb3NvbWVzIC0+IAogICAgI2lkZW50aWZ5IGZyYWdtZW50cyBhcyBzdWItbnVjbGVvc29tZSBzcGFubmluZywgbW9uby1udWNsZW9zb21lIHNwYW5uaW5nIG9yIG11bHRpLW51Y2xlb3NvbWUgc3Bhbm5pbmcKICBudWNMZW5ndGggPSAxNDcsIAogICMgaW50ZWdlciB2ZWN0b3IgLT4gZGVmaW5lIHJlZ2lvbiB1cC9kb3duc3RyZWFtIG9mIFRTUyB0byBpbmNsdWRlIGFzIHByb21vdGVyIHJlZ2lvbgogICMgY2FuIGJlIHVzZWQgdG8gY2FsY3VsYXRlIGUuZyBmcmFjdGlvbiBvZiByZWFkcyBpbiBwcm9tb3RlciAoRklQKQogIHByb21vdGVyUmVnaW9uID0gYygyMDAwLCAxMDApLAogICMgcGFyYW1ldGVycyBmb3IgY29tcHV0aW5nIFRTUyBlbnJpY2htZW50IHNjb3Jlcywgd2luZG93IChicCkgY2VudGVyZWQgYXQgVFNTID0gMTAxCiAgIyBmbGFua2luZyB3aW5kb3cgPSAyMDAwIGJwCiAgIyBub3JtID0gc2l6ZSBvZiBmbGFuayB3aW5kb3cgdXNlZCBmb3Igbm9ybWFsaXphdGlvbiA9IDEwMCBicAogICMgYWNjZXNzaWJpbGl0eSB3aXRoaW4gMTAxIGJwIHN1cnJvdW5kaW5nIHRoZSBUU1Mgd2lsbCBiZSBub3JtYWxpemVkIHRvIGFjY2Vzc2liaWxpdHkKICAjIGluIDEwMCBicCBiaW5zIGZyb20gLTIwMDA6LTE5MDFicCBhbmQgMTkwMTogMjAwMAogIFRTU1BhcmFtcyA9IGxpc3QoMTAxLCAyMDAwLCAxMDApLAogICMgd2hpY2ggY2hyb21vc29tZXMgdG8gZXhjbHVkZSBmb3JtIGRvd25zdHJlYW0gYW5hbHlzaXMKICAjIGluIGh1bWFuIGFuZCBtb3VzZTogbWl0b2Nob25kcmlhbCBETkEgKGNock0pIGFuZCBtYWxlIHNleCBjaHJvbW9zb21lIChjaHJZKQogICMgdGhlIGZyYWdtZW50cyBhcmUgc3RpbGwgc3RvcmVkIGluIHRoZSBhcnJvdyBmaWxlcwogIGV4Y2x1ZGVDaHIgPSBjKCJjaHJNIiwgImNoclkiKSwKICAjIG51bWJlciBvZiBjaHVua3MgdG8gZGl2aWRlIGNocm9tb3NvbWVzIGluIC0+IGxvdy1tZW1vcnkgcGFyYWxsZWxpemVkIHJlYWRpbmcgb2YgaW5wdXQgZmlsZXMKICBuQ2h1bmsgPSA1LAogICMgbmFtZSBvZiBmaWVsZCBpbiBpbnB1dCBiYW0gZmlsZSBjb250YWluaW5nIHRoZSBiYXJjb2RlIHRhZyBpbmZvcm1hdGlvbgogIGJjVGFnID0gInFuYW1lIiwKICBvZmZzZXRQbHVzID0gNCwgIyBvZmZzZXQgYXBwbGllZCB0byArIHN0cmFuZGVkIFRuNSBpbnNlcnRpb24gLT4gYWNjb3VudCBmb3IgcHJlY2lzZSBUbjUgYmluZGluZyBzaXRlCiAgb2Zmc2V0TWludXMgPSAtNSwgCiAgbG9nRmlsZSA9IGNyZWF0ZUxvZ0ZpbGUoImNyZWF0ZUFycm93cyIpCikKYGBgCgpgYGB7cn0KIyB0aGUgYXJyb3cgZmlsZXMgb2JqZWN0IHNpbXBseSBjb250YWlucyBhIGNoYXJhY3RlciB2ZWN0b3Igb2YgYXJyb3cgZmlsZSBwYXRocwpBcnJvd0ZpbGVzCmBgYAoKCiMjIFF1YWxpdHkgQ29udHJvbAoKSGF2ZSBhIGxvb2sgYXQgdGhpcyBmb3IgYWRkaXRpb25hbCBRQyEKaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL2RldmVsL2Jpb2MvdmlnbmV0dGVzL0FUQUNzZXFRQy9pbnN0L2RvYy9BVEFDc2VxUUMuaHRtbAoKMS4gdGhlIG51bWJlciBvZiAqKnVuaXF1ZSBudWNsZWFyIGZyYWdtZW50cyoqIChhcyBvcHBvc2VkIHRvIG1pdG9jaG9uZHJpYWwgZnJhZ21lbnRzKQpBIGNlbGwgd2l0aCB2ZXJ5IGZldyB1c2FibGUgZnJhZ21lbnRzIHdpbGwgbm90IHByb3ZpZGUgZW5vdWdoIGRhdGEgdG8gbWFrIHVzZWZ1bCBjb25jbHVzaW9ucy4KMi4gKipzaWduYWwtdG8tYmFja2dyb3VuZCByYXRpbyoqIC0+IGlmIHRoaXMgaXMgbG93IHRoaXMgcHJvYmFibHkgY29ycmVzcG9uZHMgdG8gZHlpbmcKY2VsbHMgd2hlcmUgdGhlIGVudGlyZSBnZW5vbWUgYWxsb3dzIHJhbmRvbSB0cmFuc3Bvc2l0aW9uCjMuICoqZnJhZ21lbnQgc2l6ZSBkaXN0cmlidXRpb24qKiAtPiBzaW5jZSAxNDcgYnAgYXJlIHdyYXBwZWQgYXJvdW5kIGEgbnVjbGVvc29tZSBpdCBpcyAKZXhwZWN0ZWQgdGhhdCB0aGVyZSBhcmUgZGVwbGV0aW9ucyBvZiBmcmFnbWVudHMgb2YgdGhpcyBsZW5ndGggYXQgcmVndWxhciBpbnRlcnZhbHMuIApXZSBleHBlY3QgdG8gc2VlIGEgcGVyaW9kaWMgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZXRuIHNpemUgY29ycmVzcG9uZGluZyB0byBudWNsZW9zb21lcwoobW9ubywgZGksIHRyaSwgLi4uKSwgYmVjYXVzZSBUbjUgY2Fubm90IGN1dCBETkEgdGhhdCBpcyB0aWdodGx5IHdyYXBwZWQgYXJvdW5kIAphIG51Y2xlb3NvbWUuIAoKIyMjIFRTUyBlbnJpY2htZW50IHNjb3JlCj0gc2lnbmFsIHRvIG5vaXNlIGNhbGN1bGF0aW9uCgpBVEFDLXNlcSBkYXRhIGlzIGVucmljaGVkIGF0IFRTUyByZWdpb25zIGNvbXBhcmVkIHRvIG90aGVyIGdlbm9taWMgcmVnaW9ucywgYmVjYXVzZQpsYXJnZSBwcm90ZWluIGNvbXBsZXhlcyBiaW5kIHRvIHByb21vdGVycy4gVGhlcmVmb3JlLCB0aGVyZSBpcyBhIGxvY2FsIGVucmljaG1lbnQgb2YgCnBlci1iYXNlcGFpciBhY2Nlc3NpYmlsaXR5IHJlbGF0aXZlIHRvIGZsYW5raW5nIHJlZ2lvbnMuIFRoZSBzY29yZSBpcyBhcHByb3hpbWF0ZWQKZm9yIGVhY2ggY2VsbC4gVGhlIGF2ZXJhZ2UgYWNjZXNzaWJpbGl0eSB3aXRoaW4gNTAgYnAgb2YgdGhlIFRTUyBpcyBkaXZpZGVkIGJ5IHRoZSAKYXZlcmFnZSBhY2Nlc3NpYmlsaXR5IG9mIHRoZSBUU1MgZmxhbmtpbmcgcG9zaXRpb25zLiAKClRoZSByZWFkcyBhcm91bmQgYSBwYXJ0aWN1bGFyIFRTUyBhcmUgY29sbGVjdGVkIGFuZCBhZ2dyZWdhdGVkIHRvIGZvcm0gYSBkaXN0cmlidXRpb24Kb2YgcmVhZHMgd2hpY2ggaXMgY2VudGVyZWQgb24gdGhlIFRTUy4gVGhpcyBkaXN0cmlidXRpb24gZXh0ZW5kcyB0byAxMDAwYnAgaW4gYm90aCAKZGlyZWN0aW9ucwoqIHdlIHRha2UgdGhlIGF2ZXJhZ2UgcmVhZCBkZXB0aCBpbiB0aGUgMTAwIGJwcyBhdCBlYWNoIGVuZCBmbGFuayBhbmQgY2FsY3VhbHRlIAphIGZvbGQgY2hhbmdlIGF0IGVhY2ggcG9zaXRpb24gb3ZlciB0aGF0IGF2ZXJhZ2UgcmVhZCBkZXB0aCAodG90YWwgb2YgMjAwYnAgb2YgCmF2ZXJhZ2VkIGRhdGEpCiogSWYgdGhlcmUgaXMgYSBoaWdoIHJlYWQgc2lnbmFsIGF0IFRTUyB0aGVyZSB3aWxsIGJlIGFuIGluY3JlYXNlIGluIHNpZ25hbCB0b3dhcmRzCnRoZSBtaWRkbGUgb2YgdGhlIGRpc3RyaWJ1dGlvbgoqIGhpZ2ggc2lnbmFsIG1lYW5zIHRoYXQgdGhlIHJlZ2lvbiBpcyBhY2Nlc3NpYmxlCiogdGhlIHNpZ25hbCB2YWx1ZSBhdCB0aGUgY2VudGVyIG9mIHRoZSBkaXN0cmlidXRpb24gaXMgdGhlIFRTUyBlbnJpY2htZW50IG1ldHJpYwoKIyMgSW5mZXJyaW5nIERvdWJsZXRzCgpTaG91bGQgYmUgcmVtb3ZlZCwgYmVjYXVzZSB0aGV5IGNhbiBpbnRlcmZlcmUgd2l0aCBkb3duc3RyZWFtIGFuYWx5c2lzLgoKKipEb3VibGV0IGRldGVjdGlvbi1hbmQtcmVtb3ZhbCBhbGdvcml0aG06KioKSGV0ZXJvdHlwaWMgZG91YmxldHMgYXJlIGlkZW50aWZpZWQgYnkgZ2VuZXJhdGluZyBhIGNvbGxlY3Rpb24gb2Ygc3ludGhldGljIGRvdWJsZXRzLgpUaGVzZSBzeW50aGV0aWMgZG91YmxldHMgYXJlIHByb2plY3RlZCBpbnRvIGxvdy1kaW1lbnNpb25hbCBlbWJlZGRpbmdzLiBTZWFyY2hpbmcKZm9yIG5lYXJlc3QgbmVpZ2hib3VycyB0byB0aGUgc3ludGhldGljIGRvdWJsZXRzIHdlIGNhbiBpZGVudGlmeSBkb3VibGV0cyBpbiB0aGUKZGF0YXNldC4gVGhpcyBvdXRwZXJmb3JtcyB0aGUgcHJlZGljdGlvbiBvZiBkb3VibGV0cyB1c2luZyBmcmFnbWVudCBudW1iZXIKKFJPQy1BVUMpLiAoQ29tcGFyZWQgdG8gZGVtdXhsZXQgYXMgZ3JvdW5kIHRydXRoKQoKKipXZSBjYW4gYWxzbyBpZGVudGlmeSBkb3VibGV0cyBpbiB0aGUgc2NSTkEtc2VxIHNwYWNlIGlmIHdlIGhhdmUgcGFpcmVkIGRhdGEKYW5kIHJlbW92ZSB0aGUgY2VsbHMgaW4gdGhpcyB3YXkuKioKCmBgYHtyLCBmaWcud2lkdGggPSA4LCBmaWcuaGVpZ2h0PTUsIHJlc3VsdHM9ICJhc2lzIn0KIyBmb3IgZWFjaCBzYW1wbGUgcHJvdmlkZWQgZG91YmxldCBpbmZvcm1hdGlvbiB3aWxsIGJlIGFzc2lnbmVkIHRvIGVhY2ggY2VsbAojIHRoaXMgd2F5IHdlIGNhbiByZW1vdmUgZG91YmxldC1iYXNlZCBjbHVzdGVycyBkb3duc3RyZWFtCmRvdWJTY29yZXMgPC0gYWRkRG91YmxldFNjb3JlcygKICB1c2VNYXRyaXggPSAiVGlsZU1hdHJpeCIsCiAgaW5wdXQgPSBBcnJvd0ZpbGVzLAogIGsgPSAxMCwgI1JlZmVycyB0byBob3cgbWFueSBjZWxscyBuZWFyIGEgInBzZXVkby1kb3VibGV0IiB0byBjb3VudC4KICBuVHJpYWwgPSA1LCAjIG51bWJlciBvZiB0aW1lIHRvIHNpbXVsYXRlIG5DZWxsIGRvdWJsZXRzIAogIGtubk1ldGhvZCA9ICJVTUFQIiwgI1JlZmVycyB0byB0aGUgZGltZW5zaW9uYWxpdHkgcmVkdWNpdG9uIG1ldGhvZCB0byB1c2UgZm9yIG5lYXJlc3QgbmVpZ2hib3Igc2VhcmNoLgogIExTSU1ldGhvZCA9IDEgIyBvZGVyIG9mIG5vcm1hbGl6YXRpb246IHRmLWxvZyhpZGYpCikKCiMgdGhlIGRvdWJsZXQgaW5mb3JtYXRpb24gaXMgc2F2ZWQgaW4gYSBzaW1wbGVMaXN0b2JqZWN0CiMgcmVhZCBpbiB0aGUgb2JqZWN0CmRvdWJsZXRfc3VtbWFyeSA8LSByZWFkUkRTKCJRdWFsaXR5Q29udHJvbC9zY0FUQUNfQk1NQ19SMS9zY0FUQUNfQk1NQ19SMS1Eb3VibGV0LVN1bW1hcnkucmRzIikKCmRvdWJsZXRfc3VtbWFyeVtbMl1dICU+JSBoZWFkKCkgJT4lIGthYmxlKCkKCiMgZ2V0IGZpcnN0IGVudHJ5IG9mIHRoZSBsaXN0ID0gb3JpZ2luYWxEYXRhVU1BUApwMSA8LSBkb3VibGV0X3N1bW1hcnkgW1sxXV0gJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sID0gZW5yaWNobWVudCksIHNpemUgPSAuMSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9IkRvdWJsZXRFbnJpY2htZW50IikpICsKICBsYWJzKHRpdGxlID0gIlNpbXVsYXRlZCBEb3VibGV0IEVucmljaG1lbnQgb3ZlciBleHBlY3RhdGlvbiIpCgpwMiA8LSBkb3VibGV0X3N1bW1hcnkgW1sxXV0gJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sID0gc2NvcmUpLCBzaXplID0gLjEpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJEb3VibGV0U2NvcmVzIC1sb2cxMChQLWFkaikiKSkgKwogIGxhYnModGl0bGUgPSAiRG91YmxldCBTY29yZXMgLWxvZzEwKFAtYWRqKSIpCgoKcDMgPC0gZG91YmxldF9zdW1tYXJ5W1syXV0gJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geCwgeSA9IHksIGNvbCA9IGRlbnNpdHkpLCBzaXplID0gLjEpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKHRpdGxlPSJTaW11bGF0ZWQgRG91YmxldCBEZW5zaXR5IikpICsKICBsYWJzKHRpdGxlID0gIkRvdWJsZXQgZGVuc2l0eSIpCgoKcDQgPC0gZG91YmxldF9zdW1tYXJ5W1syXV0gJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0geCwgeSA9IHksIGNvbCA9IGRlbnNpdHkpLCBzaXplID0gLjEpICsKICBnZW9tX3BvaW50KGRhdGEgPSBkb3VibGV0X3N1bW1hcnlbWzFdXSwgYWVzKHggPSBYMSwgeSA9IFgyKSwgc2l6ZSA9IC4xLCBhbHBoYSA9IC40KSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19jKCkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZCh0aXRsZT0iU2ltdWxhdGVkIERvdWJsZXQgRGVuc2l0eSIpKSArCiAgbGFicyh0aXRsZSA9ICJTaW11bGF0ZWQgZG91YmxldCBkZW5zaXR5IG92ZXJsYXllZCIpCgpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCBwNCwgbmNvbCA9IDIpCgpgYGAKCgojIyBDcmVhdGUgQXJjaFJQcm9qZWN0CgpBbiBBcmNoUiBQcm9qZWN0IGlzIGluaXRpYWxpemVkIHdpdGggc29tZSBpbXBvcnRhbnQgYXR0cmlidXRlczoKCiogb3VwdXQgZGlyZWN0b3J5Ciogc2FtcGxlIG5hbWVzCiogYHNhbXBsZUNvbERhdGFgIC0+IG1hdHJpeCBjb250YWluaW50IGRhdGEgZm9yIGVhY2ggc2FtcGxlCiogYGNlbGxDb2xEYXRhYCAtPiBjb250YWlucyBkYXRhIGFzc29jaWF0ZWQgd2l0aCBlYWNoIGNlbGwKICArIGFmdGVyIHVzaW5nIGBhZGREb3VibGV0U2NvcmUoKWAgdGhlcmUgd2lsbCBiZSBhIGNvbHVtbiAKICBmb3IgIkRvdWJsZXQgRW5yaWNobWVudCIgYW5kICJEb3VibGV0IFNjb3JlIgoqIHRvdGFsIG51bWJlciBvZiBjZWxscyAoZXhjbHVkaW5nIGRvdWJsZXRzKQoqIG1lZGlhbiBUU1Mgc2NvcmUgJiBtZWRpYW4gbnVtYmVyIG9mIGZyYWdtZW50cyBhY3Jvc3MgYWxsIGNlbGxzIAphbmQgc2FtcGxlcwoKYGBge3J9CnByb2ogPC0gQXJjaFJQcm9qZWN0KAogIEFycm93RmlsZXMgPSBBcnJvd0ZpbGVzLCAKICBvdXRwdXREaXJlY3RvcnkgPSAiQXJjaFJWaWduZXR0ZSIsCiAgY29weUFycm93cyA9IFRSVUUsICNUaGlzIGlzIHJlY29tbWVuZWQgc28gdGhhdCB5b3UgbWFpbnRhaW4gYW4gdW5hbHRlcmVkIGNvcHkgZm9yIGxhdGVyIHVzYWdlLgogIGdlbmVBbm5vdGF0aW9uID0gZ2V0R2VuZUFubm90YXRpb24oKSwKICAjZ2Vub21lQW5ub3RhdGlvbiA9IGdldEdlbmVBbm5vdGF0aW9uKCksCiAgc2hvd0xvZ28gPSBGQUxTRQopCmBgYAoKIyMjIEFjY2VzcyBBcmNoUjoKCkNlbGwgTmFtZXM6CgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBjZWxsIG5hbWVzCnByb2okY2VsbE5hbWVzWzE6NV0KYGBgCgpTYW1wbGUgTmFtZXM6CgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KcHJvaiRTYW1wbGVbMTo1XQpsZW5ndGgocHJvaiRTYW1wbGUpCmBgYAoKRm9yIGVhY2ggY2VsbCB0aGVyZSBpcyBhIFRTUyBlbnJpY2htZW50IHNjb3JlOgpJcyB0aGlzIHRoZSBhdmVyYWdlIFRTUyBlbnJpY2htZW50IHNjb3JlIGZvciBlYWNoIGNlbGw/CgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBUU1MgRW5yaWNobWVudCBzY29yZQpwcm9qJFRTU0VucmljaG1lbnQgJT4lIGhlYWQKcXVhbnRpbGUocHJvaiRUU1NFbnJpY2htZW50KQoKCgpoaXN0KHByb2okVFNTRW5yaWNobWVudCwgeGxhYiA9ICJUU1MgZW5yaWNobWVudCBzY29yZSIsIG1haW4gPSAiVFNTIEVucmljaG1lbnQgU2NvcmUiLCBicmVha3MgPSAxMDApCgojIHJlYWRzIGluIFRTUwpoaXN0KHByb2okUmVhZHNJblRTUywgeGxhYiA9ICJSZWFkcyBpbiBUU1MiLCBtYWluID0gIlJlYWRzIGluIFRTUyIsIGJyZWFrcyA9IDEwMCkgCmhpc3QocHJvaiRSZWFkc0luUHJvbW90ZXIsIHhsYWIgPSAiUmVhZHMgaW4gUHJvbW90ZXIiLCBtYWluID0gIlJlYWRzIGluIFByb21vdGVyIiwgYnJlYWtzID0gMTAwKQpoaXN0KHByb2okUmVhZHNJbkJsYWNrbGlzdCwgeGxhYiA9ICJSZWFkcyBpbiBCbGNrbGlzdCIsIG1haW4gPSJSZWFkcyBpbiBCbGFja2xpc3QiLCBicmVha3MgPSAxMDApCmBgYAoKbnVtYmVyIG9mIGZyYWdtZW50czoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdycsfQojIG51bWJlciBvZiBmcmFnbWV0bnMgZm9yIGVhY2ggY2VsbApwcm9qJG5GcmFncyAlPiUgaGVhZApxdWFudGlsZShwcm9qJG5GcmFncykKCgpwMSA8LSBnZ3Bsb3QoYXNfdGliYmxlKHByb2okbkZyYWdzKSwgYWVzKHggPSB2YWx1ZSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDApICsKICBsYWJzKHRpdGxlID0gIm51bWJlciBvZiB1bmlxdWUgZnJhZ21lbnRzIikgKwogIHhsYWIoIm51bWJlciBvZiB1bmlxdWUgZnJhZ21lbnRzIikKcDIgPC0gZ2dwbG90KGFzX3RpYmJsZShwcm9qJG5NdWx0aUZyYWdzKSwgYWVzKHggPSB2YWx1ZSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDApICsKICBsYWJzKHRpdGxlID0gIm51bWJlciBvZiBtdWxpdC1udWNsZW9zb21lIGZyYWdtZW50cyIpICsKICB4bGFiKCJudW1iZXIgb2YgbXVsaXQtbnVjbGVvc29tZSBmcmFnbWVudHMiKQpwMyA8LSBnZ3Bsb3QoYXNfdGliYmxlKHByb2okbk1vbm9GcmFncyksIGFlcyh4ID0gdmFsdWUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwKSArCiAgbGFicyh0aXRsZSA9ICJudW1iZXIgb2YgbW9uby1udWNsZW9zb21lIGZyYWdtZW50cyIpICsKICB4bGFiKCJudW1iZXIgb2YgbW9uby1udWNlbG9zb21lIGZyYWdtZW50cyIpCnA0IDwtIGdncGxvdChhc190aWJibGUocHJvaiRuRGlGcmFncyksIGFlcyh4ID0gdmFsdWUpKSArIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMTAwKSArCiAgbGFicyh0aXRsZSA9ICJudW1iZXIgb2YgRGktbnVjbGVvc29tZSBmcmFnbWVudHMiKSArCiAgeGxhYigibnVtYmVyIERpLWZyYWdtZW50cyIpCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgcDMscDQsIG5jb2wgPSAyKQpgYGAKCgojIyBIb3cgdG8gc3Vic2V0IGFuIEFyY2hSIHByb2plY3QKCiogbnVtZXJpY2FsbHkgCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KIyBub3RlIHRoYXQgdGhhdCBvbmx5IDEwMCBjZWxscyBhcmUgYWNjZXNzZWQKcHJvalsxOjEwMCxdCmBgYAoKCiogYnkgY2VsbCBuYW1lCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KcHJvaltwcm9qJGNlbGxOYW1lc1sxOjEwMF0sIF0KYGBgCgoqIGtlZXAgc3BlY2lmaWMgc2FtcGxlcwoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CmlkeFNhbXBsZSA8LSBCaW9jR2VuZXJpY3M6OndoaWNoKHByb2okU2FtcGxlICVpbiUgInNjQVRBQ19CTU1DX1IxIikKY2VsbHNTYW1wbGUgPC0gcHJvaiRjZWxsTmFtZXNbaWR4U2FtcGxlXQpwcm9qW2NlbGxzU2FtcGxlLCBdCmBgYAoKKiBzcGVjaWZpYyBjdXRvZmYgZm9yIFRTUyBlbnJpY2htZW50IHNjb3JlCgpgYGB7ciBjbGFzcy5zb3VyY2UgPSAnZm9sZC1zaG93J30KaWR4UGFzcyA8LSB3aGljaChwcm9qJFRTU0VucmljaG1lbnQgPj0gOCkKY2VsbHNQYXNzIDwtIHByb2okY2VsbE5hbWVzW2lkeFBhc3NdCnByb2pbY2VsbHNQYXNzLCBdCmBgYAoKCmBgYHtyfQpnZXRBdmFpbGFibGVNYXRyaWNlcyhwcm9qKQpgYGAKCgojIyMgT2J0YWluaW5nIGNvbHVtbnMgZnJvbSBjZWxsQ29sRGF0YQoKKiByZXRyaWV2ZSBtZXRhZGF0YSBjb2x1bW5zLCBlLmcuIG51bWJlciBvZiB1bmlxdWUgbnVjbGVhciBmcmFnbWVudHMgcGVyIGNlbGwKCmBgYHtyLCAgcmVzdWx0cyA9ICJhc2lzIn0KZGYgPC0gZ2V0Q2VsbENvbERhdGEocHJvaiwgc2VsZWN0ID0gIm5GcmFncyIpCmRmICU+JSBoZWFkICU+JSBrYWJsZSgpCmBgYAoKKiBvcGVyYXRpb25zIG9uIGEgZ2l2ZW4gY29sdW1uCgpgYGB7ciwgIHJlc3VsdHMgPSAiYXNpcyJ9CmRmIDwtIGdldENlbGxDb2xEYXRhKHByb2osIHNlbGVjdCA9IGMoImxvZzEwKG5GcmFncykiLCAibkZyYWdzIC0xICIpKQoKZGYgJT4lIGhlYWQgJT4lIGthYmxlKCkKYGBgCgojIFBsb3QgUUMgbWV0cmljcyAKCkRhdGEgYmVmb3JlIFFDIGFuZCBjb3JyZXNwb25kaW5nIHBsb3RzIGFyZSBzYXZlZCBpbiB0aGUgUXVhbGl0eSBDb250cm9sIG91dHB1dCBmb2xkZXIuCgojIyBsb2cxMCh1bmlxdWUgZnJhZ21lbnRzKSB2cyBUU1MgZW5yaWNobWVudAoKKiBUU1MgZW5yaWNobWVudCBzY29yZSA9IHNpZ25hbC10by1iYWNrZ3JvdW5kIAoqIG51bWJlciBvZiB1bmlxdWUgZnJhZ21lbnRzIC0+IGNlbGxzIHdpdGggdmVyeSBmZXcgZnJhZ21lbnRzIGRvIG5vdCBoYXZlIGVub3VnaCAKZGF0YSB0byBjb25maWRlbnRseSBhbmFseXplIHRoZW0gCiogaW4gdGhlIHBsb3QgYXJlYXMgd2l0aCBtb3JlIHBvaW50cy9jZWxscyBhcmUgY29sb3JlZCBpbiBvcmFuZ2UsIGFuZCBhcmVhcwp3aXRoIGxlc3MgcG9pbnRzIGluIGJsdWUsIGluZGljYXRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBjZWxsCgoKYGBge3IsIGZpZy53aWR0aD04fQojY3JlYXRlIDMgc2VwYXJhdGUgZGF0YWZyYW1lcyBmb3IgYWxsIHNhbXBsZXMKdGhyZWVfc2FtcGxlcyA8LSBtYXAodW5pcXVlKHByb2okU2FtcGxlKSwgZnVuY3Rpb24obmFtZSl7CiAgaW5kZXggPC0gQmlvY0dlbmVyaWNzOjp3aGljaChwcm9qJFNhbXBsZSAlaW4lIG5hbWUpCiAgY2VsbHMgPC0gcHJvaiRjZWxsTmFtZXNbaW5kZXhdCiAgc2FtcGxlX3N1YnNldCA8LSBwcm9qW2NlbGxzXQogIGRmIDwtIGdldENlbGxDb2xEYXRhKHNhbXBsZV9zdWJzZXQsIHNlbGVjdCA9IGMoImxvZzEwKG5GcmFncykiLCAiVFNTRW5yaWNobWVudCIpKQogIHAgPC0gZ2dQb2ludCgKICAgIHggPSBkZlssIDFdLCB5ID0gZGZbLCAyXSwgCiAgICBjb2xvckRlbnNpdHkgPSBUUlVFLCAjIHNob3VsZCB0aGUgZGVuc2l0eSBvZiBwb2ludHMgb24gdGhlIHBsb3QgYmUgaW5kaWNhdGVkIGJ5IGNvbG9yPwogICAgY29udGludW91c1NldCA9ICJzYW1iYU5pZ2h0IiwgCiAgICB4bGFiZWwgPSAiTG9nMTAgdW5pcXVlIGZyYWdtZW50cyIsCiAgICB5bGFiZWwgPSAiVFNTIGVucmljaG1lbnQiLAogICAgdGl0bGUgPSBwYXN0ZTAoIlNhbXBsZTogIiwgbmFtZSksCiAgICB4bGltID0gYyhsb2cxMCg1MDApLCBxdWFudGlsZShkZlssMV0sIHByb2JzID0gMC45OSkpLAogICAgeWxpbSA9IGMoMCwgcXVhbnRpbGUoZGZbLDJdLCBwcm9icyA9IDAuOTkpKQogICAgKSArIGdlb21faGxpbmUoeWludGVyY2VwdCA9IDQsIGx0eSA9ICJkYXNoZWQiKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAzLCBsdHkgPSAiZGFzaGVkIikKICBsaXN0KHBsb3QgPSBwLCBuYW1lID0gbmFtZSkKfSkKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHRocmVlX3NhbXBsZXNbWzFdXSRwbG90LCB0aHJlZV9zYW1wbGVzW1syXV0kcGxvdCwgCiAgICAgICAgICAgICAgICAgICAgICAgIHRocmVlX3NhbXBsZXNbWzNdXSRwbG90LCBuY29sID0gMykKCgpgYGAKCldlIHdhbnQgYSBUU1MgZW5yaWNobWVudCBzY29yZSBvZiA+IDQgYW5kIGEgbnVtYmVyIG9mIHVuaXF1ZSBmcmFnbWVudHMgPiAxMDAwIChsb2cxMCgxMDAwKSA9IDMpLiAKCgojIyMgUGxvdHRpbmcgc2FtcGxlIHN0YXRpc3RpY3MKCiogd2hlbiB3ZSBoYXZlIGRpc3RpbmN0IHNhbXBsZXMsIGl0IGNhbiBiZSBpbXBvcnRhbnQgdG8gY29tcGFyZSB2YXJpb3VzCm1ldHJpYyBiZXR3ZWVuIHNhbXBsZXMKKiByaWRnZSBwbG90cyAmIHZpb2xpbiBwbG90cyBhcmUgdXNlZCBmb3IgZ3JvdXBlZCBkYXRhCgpgYGB7cixmaWcud2lkdGg9MTB9CgpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IFRTU0VucmljaG1lbnQsIGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuOCkgCgpwMiA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdncmlkZ2VzOjpnZW9tX2RlbnNpdHlfcmlkZ2VzKGFlcyh4ID0gVFNTRW5yaWNobWVudCwgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihhZXMoeCA9IFNhbXBsZSwgeSA9IFRTU0VucmljaG1lbnQsIGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuOCkgKwogIGdlb21fYm94cGxvdChhZXMoeCA9IFNhbXBsZSwgeSA9IFRTU0VucmljaG1lbnQsZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC40KSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiVFNTIEVucmljaG1lbnQiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKYGBge3IsZmlnLndpZHRoPTEwfQpwMSA8LSBhc19kYXRhX2ZyYW1lKGdldENlbGxDb2xEYXRhKHByb2opKSAlPiUgCiAgbXV0YXRlKFNhbXBsZSA9IHN0cl9yZW1vdmUoU2FtcGxlLCAic2NBVEFDXyIpLCBsb2cxMF9uRnJhZ3MgPSBsb2cxMChuRnJhZ3MpKSAlPiUgCiAgZ2dwbG90KCkgKwogIGdlb21fZGVuc2l0eShhZXMoeCA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KQoKcDIgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZ3JpZGdlczo6Z2VvbV9kZW5zaXR5X3JpZGdlcyhhZXMoeCA9IGxvZzEwX25GcmFncywgeSA9IFNhbXBsZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKcDMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSwgbG9nMTBfbkZyYWdzID0gbG9nMTAobkZyYWdzKSkgJT4lIAogIGdncGxvdCgpICsKICBnZW9tX3Zpb2xpbihhZXMoeCA9IFNhbXBsZSwgeSA9IGxvZzEwX25GcmFncywgZmlsbCA9IFNhbXBsZSksIGFscGhhID0gMC44KSArCiAgZ2VvbV9ib3hwbG90KGFlcyh4ID0gU2FtcGxlLCB5ID0gbG9nMTBfbkZyYWdzLGZpbGwgPSBTYW1wbGUpLCBhbHBoYSA9IDAuNCkgKyAKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIm51bWJlciBvZiBmcmFnbWVudHMiKQoKCmNvd3Bsb3Q6OnBsb3RfZ3JpZChwMywgcDIsIHAxLCBuY29sID0gMykKYGBgCgoKIyMjIFBsb3QgRnJhZ21lbnQgU2l6ZSBEaXN0cmlidXRpb24gJiBUU1MgRW5yaWNobWVudCBQcm9maWxlcwoKKiB0aGUgZGlzdHJpYnV0aW9uIG9mIGZyYWdtZW50cyBzaXplIGNhbiBiZSB2ZXJ5IGRpZmZlcmVudCBiZXR3ZWVuIHNhbXBsZXMsCmNlbGwgdHlwZXMgYW5kIGJhdGNoZXMgLT4gdGhlc2UgZGlmZmVyZW5jZXMgZG8gbm90IG5lY2Vzc2FyaWx5IGNvcnJlbGF0ZSB3aXRoIApkaWZmZXJlbmNlcyBpbiBxdWFsaXR5CiogdGhlIGRpcCBpcyB0aGUgZnJhZ21lbnQgc2l6ZSBvZiBhIG51Y2xlb3NvbWUgfjE0N2JwCiogVFNTIGVucmljaG1lbnQgcHJvZmlsZXMKICArIGNsZWFyIHBlYWsgaW4gdGhlIGNlbnRlciAKICArIHNtYWxsZXIgc2hvdWxkZXIgcGVhayByaWdodCBvZiB0aGUgY2VudGVyIGNhdXNlZCBieSB3ZWxsIHBvc2l0aW9uZWQgKzEgbnVjbGVvc29tZQoKYGBge3IsIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0PTV9CnAxIDwtIHBsb3RGcmFnbWVudFNpemVzKEFyY2hSUHJvaiA9IHByb2opCnAyIDwtIHBsb3RUU1NFbnJpY2htZW50KEFyY2hSUHJvaiA9IHByb2opCmdnQWxpZ25QbG90cyhwMSwgcDIsIHR5cGUgPSAidiIpCmBgYAoKIyBGaWx0ZXJpbmcgRG91YmxldHMKCldpdGggdGhlIGZ1bmN0aW9uIGBhZGREb3VibGVTY29yZXMoKWAgaW5mb3JtYXRpb24gb24gcHJlZGljdGVkIGRvdWJsZXRzIGhhcyBiZWVuCmFkZGVkLiBGaWx0ZXIgdGhlIHB1dGF0aXZlIGRvdWJsZXRzLiBUaGV5IGFyZSBub3QgcmVtb3ZlZCBwaHlzaWNhbGx5LCBidXQgCmV4Y2x1ZGVkIGZyb20gZG93bnN0cmVhbSBhbmFseXNpcy4gQXJjaFIgYXV0b21hdGljYWxseSBwcmludHMgdGhlIG51bWJlciBvZgpjZWxscyByZW1vdmVkIGZyb20gZWFjaCBzYW1wbGUgYW5kIHRoZSBjb3JyZXNwb25kaW5nCnBlcmNlbnRhZ2Ugd2hpY2ggaXMgdmVyeSBoYW5keS4KCioqYXJndW1lbnRzOioqCgoqIGN1dEVucmljaCA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0RW5yaWNobWVudCwgbnVtYmVyIG9mIHNpbXVsYXRlZCAKZG91YmxldHMgZGl2aWRlZCBieSBleHBlY3RlZCBudW1iZXIgZ2l2ZW4gYSByYW5kb20gdW5pZm9ybSBkaXN0cmlidXRpb24KKiBjdXRTY29yZSA9IG1pbmltdW0gY3V0b2ZmIGZvciBEb3VibGV0IFNjb3JlLCByZXByZXNlbnRzIC1sb2cxMChiaW5vbWlhbCBhZGp1c3RlZCBwLXZhbHVlKQpmb3IgdGhlIERvdWJsZXRFbnJpY2htZW50YWRkCiogZmlsdGVyUmF0aW8gPSBtYXhpbXVtIHJhdGlvIG9mIHByZWRpY3RlZCBkb3VibGV0cyB0byBmaWx0ZXIgYmFzZWQgb24gbnVtYmVyIG9mIApwYXNzLWZpbHRlciBjZWxscyAoQSBoaWdoZXIgZmlsdGVyUmF0aW8gbWVhbnMgdGhhdCBtb3JlIGNlbGxzIGFyZSByZW1vdmVkKQplLmcuIDUwMDAgY2VsbHMKCm1heGltdW0gd291bGQgYmUgZmlsdGVyUmF0aW8gKiA1MDAwIC8gMTAwMDAwID0gZmlsdGVyUmF0aW8gKiA1MDAwICogMC4wNQoKKipUaGlzIHdheSBzYW1wbGVzIHdpdGggZGlmZmVyZW50IHBlcmNlbnRhZ2Ugb2YgZG91YmxldHMgd2lsbCBiZSBmaWx0ZXJlZCBhY2NvcmRpbmdseS4qKgoKYGBge3J9CiMgaW4gb3VyIGNhc2Ugd2Ugbm93IGhhdmUgMTAgMjUxIGNlbGxzIGFzIG9wcG9zZWQgdG8gMTAgNjYxIGNlbGxzIGJlZm9yZQojIGZpbHRlcmluZyAtPiA0MTAgY2VsbHMgd2VyZSByZW1vdmVkICgzLjg1JSkKcHJvaiA8LSBmaWx0ZXJEb3VibGV0cyhBcmNoUlByb2ogPSBwcm9qKQpgYGAKCgojIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAmIENsdXN0ZXJpbmcKCiogdHdvIG90aGVyIGFsZ29yaXRobXM6CiAgKyBsYXRlbnQgc2VtYW50aWMgaW5kZXhpbmcgKExTSSkgaW4gU2lnbmFjCiAgKyBsYW5kbWFyayBkaWZmdXNpb24gbWFwcyAoTERNKSBpbiBTbmFwQVRBQwogIAoqIEFyY2hSOiBvcHRpbWl6ZWQgaXRlcmF0aXZlIExTSSBtZXRob2QgCiAgKyBleGhpYml0cyBsZXNzIHN1c2NlcHRpYmlsaXR5IHRvIGJhdGNoIGVmZmVjdHMgCiAgKyBmb2N1c2VzIG9uIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgCiAgMS4gY3JlYXRlIGEgTFNJIFJlZHVjdGlvbiBmcm9tIGEgc3Vic2V0IG9mIHRoZSB0b3RhbCBjZWxscwogIDIuIGxpbmVhcmx5IHByb2plY3QgdGhlIHJlbWFpbmluZyBjZWxscyBpbnRvIHRoaXMgc3Vic3BhY2Ugd2l0aCBMU0kgcHJvamVjdGlvbgogIChiYXNlZCBvbiBTVkQpCgpCZWNhdXNlIHdlIGNhbiBoYXZlIG1heGltYWxseSB0d28gYWNjZXNzaWJsZSBhbGxlbGVzIHBlciBjZWxsLCB0aGUgc2NBVEFDLXNlcSBkYXRhCmlzIHNwYXJzZS4gVGhlcmVmb3JlLCB0aGUgbWFqb3JpdHkgb2YgYWNjZXNzaWJsZSByZWdpb25zIGFyZSBub3QgdHJhbnNwb3NlZCwgbWVhbmluZyAKdGhhdCBtb3N0IGxvY2kgd2lsbCBoYXZlIDAgYWNjZXNzaWJsZSBhbGxlbGVzLiBBIHplcm8gY291bGQgbWVhbiAibm9uLWFjY2Vzc2libGUiIApvciAibm90IHNhbXBsZWQiLiBGb3IgbWFueSBhbmFseXNpcyB3ZSBjYW4gdXNlIGEgYmluYXJpemVkIG1hdGl4LiAqKkltcG9yYW50bHksKioKKip0aGUgMXMgaGF2ZSBpbmZvcm1hdGlvbiwgQlVUIHRoZSAwcyBkbyBub3QhKioKCkEgUENBIHdvdWxkIHJlc3VsdCBpbiBoaWdoIGludGVyLWNlbGwgc2ltaWxhcml0eSBhdCBhbGwgMCBwb3NpdGlvbnMuIEFuIGFsdGVybmF0aXZlCmFwcHJvYWNoIGZvciBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gaXMgYSAqKmxheWVyZWQgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uKiouIEZpcnN0LAoqKkxhdGVudCBTZW1hbnRpYyBJbmRleGluZyAoTFNJKSoqIGlzIHVzZWQuIExTSSBpcyBhbiBhcHByb2FjaCBmcm9tIGxhbmd1YWdlCnByb2Nlc3NpbmcuIERpZmZlcmVudCBzYW1wbGVzIGFyZSB0aGUgImRvY3VtZW50cyIgYW5kIGRpZmZlcmVudCByZWdpb25zL3BlYWtzIGFyZQp0aGUgIndvcmRzIi4gCgojIyBJdGVyYXRpdmUgTFNJCgoxLiBjb21wdXRlIHRlcm0gZnJlcXVlbmN5IChkZXB0aCBub3JtYWxpemF0aW9uIHRvIDEwLDAwMCBwZXIgc2luZ2xlIGNlbGwpCgokVEYgPSBcZnJhY3tDX3tpan19e0Zfe2p9fSQgd2l0aCAkQ197aWp9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cyBmb3IgcGVhayBpIGluIGNlbGwgaiBhbmQgJEZfe2p9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdW50cyBpbiBjZWxsIGouCgoyLiBJbnZlcnNlIGRvY3VtZW50IGZyZXF1ZW5jeSAKICAqIHdlaWdodHMgZmVhdHVyZXMgYnkgaG93IG9mdGVuIHRoZXkgb2NjdXIgCiAgKiBtb3JlIHdlaWdodCB0byBsZXNzIGZyZXF1ZW50IHBlYWtzCgokSURGID0gXGZyYWN7Tn17bl97cH19JCB3aXRoIE4gYmVpbmcgdGhlIHRvdGFsIG51bWJlciBvZiBjZWxscyBpbiB0aGUgZGF0YXNldCBhbmQgJG5fe3B9JCBiZWluZyB0aGUgdG90YWwgbnVtYmVyIG9mIGNvdXRucyBmb3IgcGVhayBpIGFjcm9zcyBhbGwgY2VsbHMuCgozLiBUaGUgdGVybSBmcmVxdWVuY3kgVEYgaXMgbm9ybWFsaXplZCBieSB0aGUgaW52ZXJzZSBkb2N1bWVudCBmcmVxdW5jeSBJREYuIApZb3UgZ2V0IGEgKipURi1JREYqKiBtYXRyaXggKHRlcm0gZnJlcXVlbmN5LWludmVyc2UgZG9jdW1lbnQgZnJlcXVlbmN5KSB3aGljaAp0ZWxscyB1cyBob3cgaW1wb3J0YW50IGEgcmVnaW9uL3BlYWsgaXMgdG8gYSBzYW1wbGUuIEluIG90aGVyIHdvcmRzIHlvdSB0cmFuc2Zvcm0KYSBiaW5hcnkgbWF0cml4IHRvIGEgbm9uLWJpbmFyeSBtYXRyaXguCgokVEYtSURGID0gXGxvZ3sxICsgKFRGICogSURGKSAxMF57NH19JAoKNC4gU1ZEIGlkZW50aWZpZXMgdGhlIG1vc3QgdmFsdWFibGUgaW5mb3JtYXRpb24gYWNyb3NzIHNhbXBsZXMuIFRoZW4gCndlIGNhbiB1c2UgdGhlc2UgbW9zdCB2YWx1YWJsZSBmZWF0dXJlcyB0byByZXByZXNlbnQgdGhlIGRhdGEgaW4gYSBsb3dlciBkaW1lbnNpb25hbCBzcGFjZQo1LiBDbHVzdGVycyBhcmUgaWRlbnRpZmllZCB3aXRoIFNldXJhdCdzIFNoYXJlZCBOZWFyZXN0IE5laWdoYm9yIGNsdXN0ZXJpbmcKNi4gU3VtIGFjY2Vzc2liaWxpdHkgYWNyb3NzIGFsbCBzaW5nbGUgY2VsbHMgaW4gZWFjaCBjbHVzdGVyIC0+IGxvZy1ub3JtYWxpemUKNy4gSWRlbnRpZnkgbW9zdCB2YXJpYWJsZSBmZWF0dXJlcyBhY3Jvc3MgdGhlIGNsdXN0ZXJzCjguIHJlcGVhdCB3aXRoIG1vc3QgdmFyaWFibGUgcGVha3MgYXMgZmVhdHVyZXMKCldpdGggTFNJIHdlIGNhbiByZWR1Y2UgdGhlIGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBzcGFyc2UgaW5zZXJ0aW9uIG1hdHJpeCB0byB0ZW5zIApvciBodW5kcmVkcy4gVGhlbiBVTUFQIG9yIHQtU05FIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSB0aGUgZGF0YQoKClVubGlrZSBpbiBzY1JOQS1zZXEgd2UgY2Fubm90IHNlbGVjdCB0aGUgdG9wIGhpZ2hseSB2YXJpYWJsZSBmZWF0dXJlcyBiZWZvcmUgCmRpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiAoaGlnaCBub2lzZSwgbG93IHJlcHJvZHVjaWJpbGl0eSkuIFJhdGhlciB0aGUgaXRlcmF0aXZlIApMU0kgYXBwcm9hY2ggZmlyc3QgY29tcHV0ZXMgYSBMU0kgCm9uIHRoZSBtb3N0IGFjY2Vzc2libGUgdGlsZXMgKHRoaXMgd2lsbCBpZGVudGlmeSBjbHVzdGVycyBjb3JyZXNwb25kaW5nIHRvIHRoZSAKbWFqb3IgY2VsbCB0eXBlcykuIFRoZW4sIEFyY2hSIGNvbXB1dGVzIHRoZSBhdmVyYWdlIGFjY2Vzc2liaWxpdHkgYWNyb3NzIHRoZXNlIApjbHVzdGVycyBhY3Jvc3MgYWxsIGZlYXR1cmVzLiBOZXh0LCB0aGUgbW9zdCB2YXJpYWJsZSBwZWFrcyBhY3Jvc3MgdGhlc2UgY2x1c3RlcnMKYXJlIGlkZW50aWZpZWQuIFRoZSBtb3N0IGhpZ2hseSBhY2Nlc3NpYmxlIHBlYWtzIGFyZSB0aGUgZmVhdHVyZXMgb2YgYSBuZXcgCnJvdW5kIG9mIExTSS4gV2UgY2FuIHNldCBob3cgbWFueSByb3VuZHMgb2YgTFNJIHdlIHdhbnQgdG8gYmUgcGVmb3JtZWQuIAoKVXNpbmcgaXRlcmF0aXZlIExTSSByZWR1Y2VzIGJhdGNoIGVmZmVjdHMuIElmIHlvdSBzZWUgc29tZSBiYXRjaCBlZmZlY3RzIHlvdSBjb3VsZAp0cnkgdG8gYWRkIG1vcmUgTFNJIGl0ZXJhdGlvbnMgYW5kIHN0YXJ0IGZyb20gYSBsb3dlciBpbml0aWFsIGNsdXN0ZXJpbmcgCnJlc29sdXRpb24uIEFsc28sIHRoZSBudW1iZXIgb2YgdmFyaWFibGUgZmVhdHVyZXMgY2FuIGJlIGxvd2VyZWQuICAKCiMjIEVzdGltYXRlZCBMU0kKCiogZm9yIGV4dHJlbWVseSBsYXJnZSBzY0FUQUMtc2VxIGRhdGFzZXRzCgoxLiBzdWJzZXQgb2YgbGFuZG1hcmsgY2VsbHMgKHJhbmRvbWx5IHNlbGVjdGVkKSBhcmUgdXNlZCBmb3IgTFNJCjIuIHJlbWFpbmluZyBjZWxscyBhcmUgVEYtSURGIG5vcm1sYWl6ZWQgdXNpbmcgdGhlIElERiBkZXRlcm1pbmVkIGZyb20KdGhlIGxhbmRtYXJrIGNlbGxzIGZyb20gc3RlcCBvbmUKMy4gbm9ybWFsaXplZCBjZWxscyBhcmUgcG9yamVjdGVkIGludG8gdGhlIFNWRCBzcGFjZSBkZWZpbmVkIGJ5IHRoZSBsYW5kbWFyayBjZWxscwoKVGhlIExTSSB0cmFuc2Zvcm1hdGlvbiBpcywgdGhlcmVmb3JlLCBiYXNlZCBvbiBhIHN1YnNldCBvZiBjZWxscyBvbmx5IGFuZCB0aGUgCnJlbWFpbmluZyBjZWxscyBhcmUgcHJvamVjdGVkIGludG8gdGhpcyBsYW5kbWFyayBMU0kuIFRoaXMgYXBwcm9hY2ggbWluaW1pemVzIAptZW1vcnkgdXNhZ2UuIFRoZSBsYW5kbWFyayBzZXQgc2l6ZSBkZXBlbmRzIG9uIGNlbGwgdHlwZSBwcm9wb3J0aW9ucy4gCgoqIGl0ZXJhdGl2ZSBMU0kgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIC0+IGV4cGxvcmUgdGhlIHBhcmFtZXRlcnMhCgojIyBCYXRjaCBjb3JyZWN0IHdpdGggSGFybW9ueQoKKiBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gb2JqZWN0IGZyb20gQXJjaFIgaXNwIHBhc3NlZCB0byBIYXJtb255CgpgYGB7cn0KcHJvaiA8LSBhZGRJdGVyYXRpdmVMU0koQXJjaFJQcm9qID0gcHJvaiwgdXNlTWF0cml4ID0gIlRpbGVNYXRyaXgiLCBuYW1lID0gIkl0ZXJhdGl2ZUxTSSIpCgpgYGAKCmBgYApwcm9qIDwtIGFkZEl0ZXJhdGl2ZUxTSSgKICAgIEFyY2hSUHJvaiA9IHByb2osCiAgICB1c2VNYXRyaXggPSAiVGlsZU1hdHJpeCIsIAogICAgbmFtZSA9ICJJdGVyYXRpdmVMU0kiLCAKICAgIGl0ZXJhdGlvbnMgPSAyLCAKICAgIGNsdXN0ZXJQYXJhbXMgPSBsaXN0KCAjU2VlIFNldXJhdDo6RmluZENsdXN0ZXJzCiAgICAgICAgcmVzb2x1dGlvbiA9IGMoMC4yKSwgCiAgICAgICAgc2FtcGxlQ2VsbHMgPSAxMDAwMCwgCiAgICAgICAgbi5zdGFydCA9IDEwCiAgICApLCAKICAgIHZhckZlYXR1cmVzID0gMjUwMDAsIAogICAgZGltc1RvVXNlID0gMTozMAopCmBgYAoKIyMgQ2x1c3RlcmluZwoKQ2FsbGluZyBjbHVzdGVycyBpbiB0aGlzIG5ldyBzcGFjZSB1c2VzIHRoZSBTZXVyYXQncyBncmFwaCBjbHVzdGVyaW5nIGZ1bmN0aW9uCmFzIGRlZmF1bHQgY2x1c3RlcmluZyBtZXRob2QuIFRoZSBTZXVyYXQgCm1ldGhvZCBmaXJzdCBjb21wdXRzIEtOTiBncmFwaCBhbmQgdGhlbiBhICBtb2R1bGFyaXR5IG9wdGltaXphdGlvbgp0ZWNobmlxdWUgdG8gY2x1c3RlciB0aGUgY2VsbHMgKGl0ZXJhdGl2ZWx5IGdyb3VwIGNlbGxzIHRvZ2V0aGVyCndpdGggTG91dmlhbiBhbGdvcml0aG0gdXNpbmcgMTAgcmFuZG9tIHN0YXJ0cykuIEFub3RoZXIgb3B0aW9uIAppcyB0byB1c2UgIlNjcmFuIi4gVGhlCmRlZmF1bHQgbnVtYmVyIG9mIG5lYXJlc3QgbmVpZ2hib3JzIHVzZWQKaXMgMTAuIFRoZSBtaW5pbXVtIG51bWJlciBvZiBjZWxscyBmb3IgYSBjbHVzdGVyIHRvIGJlIGNhbGxlZCBhIApjbHVzdGVyIGlzIHNldCB0byA1IGJ5IGRlZmF1bHQuIFRoZSBtYXhpbXVtIG51bWJlciBvZiBjbHVzdGVycyB0byAKYmUgY2FsbGVkIGlzIHNldCB0byAyNSBieSBkZWZhdWx0LiAKCmBgYHtyfQpwcm9qIDwtIGFkZENsdXN0ZXJzKGlucHV0ID0gcHJvaiwgcmVkdWNlZERpbXMgPSAiSXRlcmF0aXZlTFNJIikKYGBgCgoKIyBWaXN1YWxpemluZyBhIFVNQVAgZW1iZWRkaW5nCgoqIHVzZXMgdXdvdCBwYWNrYWdlCiogdmFyaW91cyBhdHRyaWJ1dGVzIG9mIHRoZSBkYXRhIGNhbiBiZSB2aXN1YWxpemVkCiAgKyB0aGVzZSBhcmUgc3RvcmVkIGluIGEgbWF0cml4IGNhbGxlZCBgY2VsbENvbERhdGFgCiAgKyB3aGljaCB2YXJpYWJsZSB0aGUgcGxvdCBpcyBjb2xvcmVkIGJ5IGlzIHNwZWNpZmllZCBieSBgY29sb3JCeWAKICBhbmQgYG5hbWVgIHBhcmFtZXRlcgoKYGBge3J9CnByb2ogPC0gYWRkVU1BUChBcmNoUlByb2ogPSBwcm9qLCByZWR1Y2VkRGltcyA9ICJJdGVyYXRpdmVMU0kiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTB9CmRmIDwtIGFzX2RhdGFfZnJhbWUoY2JpbmQoZ2V0Q2VsbENvbERhdGEocHJvaiksIGdldEVtYmVkZGluZyhwcm9qKSkgKSAlPiUKICByZW5hbWUoYyh1bWFwMSA9IEl0ZXJhdGl2ZUxTSS5VTUFQX0RpbWVuc2lvbl8xLCB1bWFwMiAgPSBJdGVyYXRpdmVMU0kuVU1BUF9EaW1lbnNpb25fMikpCgp2YXJpYWJsZXMgPC0gYygiQ2x1c3RlcnMiLCAiU2FtcGxlIiwgIm5GcmFncyIsICJEb3VibGV0U2NvcmUiKQoKcGxvdHMxIDwtIG1hcChjKCJDbHVzdGVycyIsICJTYW1wbGUiKSwgZnVuY3Rpb24obil7CiAgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSBkZiAlPiUgcHVsbCgidW1hcDEiKSwgeSA9IGRmICU+JSBwdWxsKCJ1bWFwMiIpLCAKICAgICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbChuKSksIHNpemUgPSAuMDQpICsKICAgIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPXBhc3RlMChuKSkpICsKICAgIHhsYWIoInVtYXAxIikgKwogICAgeWxhYigidW1wYTIiKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUwKG4pKQp9KQoKcGxvdHMyIDwtIG1hcChjKCJuRnJhZ3MiLCAiRG91YmxldFNjb3JlIiksIGZ1bmN0aW9uKG4pewogIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoInVtYXAxIiksIHkgPSBkZiAlPiUgcHVsbCgidW1hcDIiKSwgCiAgICAgICAgICAgICAgICAgY29sID0gZGYgJT4lIHB1bGwobikpLCBzaXplID0gLjA0KSArCiAgICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQodGl0bGU9cGFzdGUwKG4pKSkgKwogICAgeGxhYigidW1hcDEiKSArCiAgICB5bGFiKCJ1bXBhMiIpICsKICAgIGxhYnModGl0bGUgPSBwYXN0ZTAobikpCn0pCgpkby5jYWxsKGdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlLCBjKHBsb3RzMSwgbmNvbD0yKSkjLCBucm93ID0gMikpCgpgYGAKCgoKYGBgI3tyLCBmaWcud2lkdGg9OH0KIyBjb2xvciBieSBzYW1wbGUKcDEgPC0gcGxvdEVtYmVkZGluZyhBcmNoUlByb2ogPSBwcm9qLCBjb2xvckJ5ID0gImNlbGxDb2xEYXRhIiwgbmFtZSA9ICJTYW1wbGUiLCBlbWJlZGRpbmcgPSAiVU1BUCIpCgojIGNvbG9yIGJ5IGNsdXN0ZXIKcDIgPC0gcGxvdEVtYmVkZGluZyhBcmNoUlByb2ogPSBwcm9qLCBjb2xvckJ5ID0gImNlbGxDb2xEYXRhIiwgbmFtZSA9ICJDbHVzdGVycyIsIGVtYmVkZGluZyA9ICJVTUFQIikKCmdnQWxpZ25QbG90cyhwMSwgcDIsIHR5cGUgPSAiaCIpCmBgYAoKIyBDbHVzdGVyIGFzc2lnbm1lbnQgdXNpbmcgZ2VuZSBzY29yZXMKCkZvciB0aGUgdG95IGRhdGFzZXQgbWFya2VyIGdlbmVzIG9mIGtub3duIGhlbWF0b3BvaWV0aWMgcmVndWxhdG9ycwpjYW4gYmUgdXNlZC4gVXNpbmcgTUFHSUMgd2UgYWRkIGltcHV0YXRpb24gd2VpZ2h0cyB0byBzbW9vdGggdGhlIGRyb3BvdXQgbm9pc2UgaW4gdGhlIGdlbmUgc2NvcmVzCgpgYGB7cn0KcHJvaiA8LSBhZGRJbXB1dGVXZWlnaHRzKHByb2opCmBgYAoKYGBge3J9Cm1hcmtlckdlbmVzICA8LSBjKAogICAgIkNEMzQiLCAgI0Vhcmx5IFByb2dlbml0b3IKICAgICJHQVRBMSIsICNFcnl0aHJvaWQKICAgICJQQVg1IiwgIk1TNEExIiwgIk1NRSIsICNCLUNlbGwgVHJhamVjdG9yeQogICAgIkNEMTQiLCAiTVBPIiwgI01vbm9jeXRlcwogICAgIkNEM0QiLCAiQ0Q4QSIjVENlbGxzCiAgKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTB9CnAgPC0gcGxvdEVtYmVkZGluZygKICAgIEFyY2hSUHJvaiA9IHByb2osIAogICAgY29sb3JCeSA9ICJHZW5lU2NvcmVNYXRyaXgiLCAKICAgIG5hbWUgPSBtYXJrZXJHZW5lcywgCiAgICBlbWJlZGRpbmcgPSAiVU1BUCIsCiAgICBpbXB1dGVXZWlnaHRzID0gZ2V0SW1wdXRlV2VpZ2h0cyhwcm9qKQopCmRvLmNhbGwoZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UsIGMocCwgbmNvbCA9IDMpKQpgYGAKCgojIFZpc3VhbGl6aW5nIEdlbm9tZSBCcm93c2VyIFRyYWNrcwoKQnJvd3NlIGxvY2FsIGNocm9tYXRpbiBhY2Nlc3NpYmlsaXR5IGF0IG1hcmtlciBnZW5lcy4gUGxvdCBnZW5vbWUKYnJvd3NlciB0cmFja3MgcGVyIGNsdXN0ZXIKYGBge3J9CnAgPC0gcGxvdEJyb3dzZXJUcmFjaygKICAgIEFyY2hSUHJvaiA9IHByb2osIAogICAgZ3JvdXBCeSA9ICJDbHVzdGVycyIsIAogICAgZ2VuZVN5bWJvbCA9IG1hcmtlckdlbmVzLCAKICAgIHVwc3RyZWFtID0gNTAwMDAsCiAgICBkb3duc3RyZWFtID0gNTAwMDAKKQpgYGAKCgpgYGB7cn0KZ3JpZDo6Z3JpZC5uZXdwYWdlKCkKZ3JpZDo6Z3JpZC5kcmF3KHAkQ0QxNCkKYGBgCgoKCgojIEludGVncmF0aW9uIHdpdGggc2NSTkEtc2VxCgoqIHRoZSBzY0FUQUMtc2VxIGdlbmUgc2NvcmUgbWF0cml4IGlzIGNvbXBhcmVkIHdpdGggdGhlIHNjUk5BLXNlcSBnZW5lIGV4cHJlc3Npb24KbWF0cml4CiogZm9yIHRoaXMgYWxpZ25tZW50IHRoZSBgRmluZFRyYW5zZmVyQW5jaG9ycygpYCBmdW5jdGlvbiBmcm9tIFNldXJhdCBpcyB1c2VkCiogdG8gc2NhbGUgZm9yIGxhcmdlIHNhbXBsZSBzaXplLCB0aGlzIHByb2Nlc3MgaXMgcGFyYWxsZWxpemVkCiogZm9yIGVhY2ggY2VsbCBpbiBBVEFDIHdlIGZpbmQgdGhlIGNlbGwgaW4gc2NSTkEtc2VxIHRoYXQgbG9va3MgbW9zdCBzaW1pbGFyIAotPiBhc3NpZ24gdGhlIGNvcnJlcHNvbmRpbmcgZ2VuZSBleHByZXNzaW9uIHRvIHRoYXQgY2VsbC4gCgpBcGFydCBmcm9tIHVzaW5nIHRoaXMgaW5mb3JtYXRpb24gZm9yIGlkZW50aWZ5aW5nIGNsdXN0ZXJzIHdlIGNhbiBhbHNvIHVzZSBpdCAKZm9yIGlkZW50aWZ5aW5nIHByZWRpY3RlZCBjaXMtcmVndWxhdG9yeSBlbGVtZW50cy4KCgpgYGB7cn0KaWYoIWZpbGUuZXhpc3RzKCJzY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIpKXsKICAgIGRvd25sb2FkLmZpbGUoCiAgICAgICAgdXJsID0gImh0dHBzOi8vamVmZmdyYW5qYS5zMy5hbWF6b25hd3MuY29tL0FyY2hSL1Rlc3REYXRhL3NjUk5BLUhlbWF0b3BvaWVzaXMtR3JhbmphLTIwMTkucmRzIiwKICAgICAgICBkZXN0ZmlsZSA9ICJzY1JOQS1IZW1hdG9wb2llc2lzLUdyYW5qYS0yMDE5LnJkcyIKICAgICkKfQoKIyByYW5nZWQgc3VtbWFyaXplZCBFeHBlcmltZW50CnNlUk5BIDwtIHJlYWRSRFMoInNjUk5BLUhlbWF0b3BvaWVzaXMtR3JhbmphLTIwMTkucmRzIikKc2VSTkEKYGBgCgpMZXRzIGhhdmUgYSBsb29rIGF0IHRoZSBjb3VudCBtYXRyaXg6CgpgYGB7cn0KIyBzcGFyc2UgY291bnQgbWF0cml4IG9mIHNjUk5BLXNlcQphc3NheXMoc2VSTkEpW1sxXV1bMToxMCwgMTo1XQpgYGAKCk1ldGFkYXRhIG9mIHRoZSBzY1JOQS1zZXEgZGF0YXNldDoKCldlIGFscmVhZHkgaGF2ZSBjbHVzdGVyaW5nLCB1bWFwIGVtYmVkZGluZ3MgYW5kIGNlbGwgdHlwZXMuCgpgYGB7ciwgcmVzdWx0cyA9ICJhc2lzIn0KY29sRGF0YShzZVJOQSkgJT4lIGhlYWQgJT4lIGtuaXRyOjprYWJsZSgpCmBgYAoKUGxvdCBRdWFsaXR5IE1ldHJpY3Mgb2YgdGhlIHNjUk5BLXNlcSBkYXRhc2V0OgoKYGBge3IsIGZpZy53aWR0aD0xMH0KYXNfZGF0YV9mcmFtZShjb2xEYXRhKHNlUk5BKSkgJT4lIAogIHNlbGVjdChuVU1JLCBuR2VuZSwgR3JvdXApICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9ICFHcm91cCwgbmFtZXNfdG8gPSAic3RhdCIpICU+JSAKICBnZ3Bsb3QoKSArCiAgZ2VvbV92aW9saW4oYWVzKHggPSBHcm91cCwgeSA9IHZhbHVlLCBmaWxsID0gR3JvdXApKSArCiAgZmFjZXRfd3JhcCh+c3RhdCwgc2NhbGVzID0gImZyZWUiKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICB4bGFiKCJTYW1wbGUiKQogICNwaXZvdF9sb25nZXIoY29scyA9ICFzKQpgYGAKCgpgYGB7cn0KZGYgPC0gYXNfZGF0YV9mcmFtZShjb2xEYXRhKHNlUk5BKSkKCnAxIDwtIGdncGxvdCgpICsKZ2VvbV9wb2ludChhZXMoeCA9IGRmICU+JSBwdWxsKCJVTUFQMSIpLCB5ID0gZGYgJT4lIHB1bGwoIlVNQVAyIiksIAogICAgICAgICAgICAgICBjb2wgPSBkZiAlPiUgcHVsbCgiQmlvQ2xhc3NpZmljYXRpb24iKSksIHNpemUgPSAuMDQpICsKICBndWlkZXMoY29sPWd1aWRlX2xlZ2VuZCh0aXRsZT0iQ2VsbFR5cGUiKSkgKwogIHhsYWIoInVtYXAxIikgKwogIHlsYWIoInVtcGEyIikgKwogIGxhYnModGl0bGUgPSAic2NSTkEtc2VxIGRhdGFzZXQgLSBjZWxsIHR5cGUiKQoKcDIgPC0gZ2dwbG90KCkgKwpnZW9tX3BvaW50KGFlcyh4ID0gZGYgJT4lIHB1bGwoIlVNQVAxIiksIHkgPSBkZiAlPiUgcHVsbCgiVU1BUDIiKSwgCiAgICAgICAgICAgICAgIGNvbCA9IGRmICU+JSBwdWxsKCJuR2VuZSIpKSwgc2l6ZSA9IC4wNCkgKwogIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPSJudW1iZXIgb2YgZ2VuZXMiKSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICB4bGFiKCJ1bWFwMSIpICsKICB5bGFiKCJ1bXBhMiIpICsKICBsYWJzKHRpdGxlID0gInNjUk5BLXNlcSBkYXRhc2V0IC0gZ2VuZSBudW1iZXIiKQoKcDMgPC0gZGYgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gVU1BUDEsIHkgPSBVTUFQMiwgY29sID0gR3JvdXApLCBzaXplID0gLjA0KSArCiAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9IlNhbXBsZSIpKSArCiAgeGxhYigidW1hcDEiKSArCiAgeWxhYigidW1wYTIiKSArCiAgbGFicyh0aXRsZSA9ICJzY1JOQS1zZXEgZGF0YXNldCAtIHNhbXBsZSIpCgpwMQpgYGAKCmBgYHtyLCBmaWcud2lkdGg9OH0KZ3JpZEV4dHJhOjpncmlkLmFycmFuZ2UocDIsIHAzLCBuY29sID0gMikKYGBgCgpgYGB7cn0KdGFibGUoY29sRGF0YShzZVJOQSkkQmlvQ2xhc3NpZmljYXRpb24pCmBgYAoKIyMgQ2VsbHMgZnJvbSBib3RoIG1vZGFsaXRpZXMKCmBgYCN7cn0KcDEgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIG11dGF0ZShTYW1wbGUgPSBzdHJfcmVtb3ZlKFNhbXBsZSwgInNjQVRBQ18iKSkgCiAgCiAgCmF0YWMgPC0gYXNfZGF0YV9mcmFtZShnZXRDZWxsQ29sRGF0YShwcm9qKSkgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpICU+JSAKICBtdXRhdGUoY2VsbCA9IHN0cl9leHRyYWN0KHJvd25hbWVzKGdldENlbGxDb2xEYXRhKHByb2opKSwgIig/PD0jKVteI10rIikpCmF0YWMKcm5hIDwtIGFzX2RhdGFfZnJhbWUoY29sRGF0YShzZVJOQSkpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBtdXRhdGUoY2VsbCA9IHN0cl9leHRyYWN0KHJvd25hbWVzKGNvbERhdGEoc2VSTkEpKSwgIig/PD06KVteOl0rIikpICU+JSAKICBmaWx0ZXIoY2VsbCAlaW4lIGF0YWNbWyJjZWxsIl1dKQoKY2JpbmQoYXRhY1sibkZyYWdzIl0sIHJuYVsiblVNSSJdKQogIApgYGAKCgojIyBVbmNvbnN0cmFpbmVkICYgQ29uc3RyYWluZWQgaW50ZWdyYXRpb24KCiMjIyBVbmNvbnN0cmFpbmVkIGludGVncmF0aW9uOgoKKiB0YWtlcyBhbGwgY2VsbHMgb2Ygc2NBVEFDLXNlcSBkYXRhIGFuZCBhdHRlbXB0cyB0byBhbGlnbiB0aGVtIHRvIGFueSBvZiB0aGUgCnNjUk5BLXNlcSBjZWxscwoqIHRoaXMgcHJvdmlkZXMgcHJlbGltaW5hcnkgY2x1c3RlciBpZGVudGl0aWVzIHdoaWNoIHdpbGwgc2VydmUgYXMgcHJpb3Iga25vd2xlZGdlCmZvciB0aGUgY29uc3RyYWluZWQgaW50ZWdyYXRpb24KKiB0aGUgcXVhbGl0eSBjYW4gYmUgaW1wcm92ZWQgYnkgY29uc3RyYWluaW5nIHRoZSBpbnRlZ3JhdGlvbgoKClRoZSBpbnRlZ3JhdGlvbiBtYXRyaXggd2lsbCBiZSBzdG9yZWQgaW4gdGhlIEFyY2hSIHByb2plY3QgdmlhIGBtYXRyaXhOYW1lYC4gVGhlIApvdGhlciBwYXJhbWV0ZXJzIHdpbGwgYmUgc2F2ZWQgaW4gdGhlIGNlbGxDb2xEYXRhIChuYW1lQ2VsbCA9IHN0b3JlIG1hdGNoZWQgY2VsbCBJRApmcm9tIHNjUk5BLXNlcSwgbmFtZUdyb3VwID0gc3RvcmUgZ3JvdXAgSUQgZnJvbSBzY1JOQS1zZXEsIG5hbWVTY29yZSA9IHN0b3JlIApjcm9zcy1wbGF0Zm9ybSBpbnRlZ3JhdGlvbiBzY29yZSkKCmBgYHtyfQpwcm9qIDwtIGFkZEdlbmVJbnRlZ3JhdGlvbk1hdHJpeCgKICAgIEFyY2hSUHJvaiA9IHByb2osIAogICAgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIsCiAgICBtYXRyaXhOYW1lID0gIkdlbmVJbnRlZ3JhdGlvbk1hdHJpeCIsCiAgICByZWR1Y2VkRGltcyA9ICJJdGVyYXRpdmVMU0kiLAogICAgc2VSTkEgPSBzZVJOQSwKICAgIGFkZFRvQXJyb3cgPSBGQUxTRSwKICAgIGdyb3VwUk5BID0gIkJpb0NsYXNzaWZpY2F0aW9uIiwgIyBCaW9jbGFzc2lmaWNhdGlvbiBpcyBhIGNvbHVtbiBpbgogICAgIyB0aGUgY29sRGF0YSBvZiB0aGUgc2VSTkEgb2JqZWN0LCB0aGlzIGNvbHVtbiB3aWxsIGJlIHVzZWQgdG8gCiAgICAjIGRldGVybWluZSB0aGUgc3ViZ3JvdXBpbmdzIHNwZWNpZmllZCBpbiBncm91cExpc3QgZm9yIGNvbnN0cmFpbmVkIGludGVncmF0aW9uCiAgICAjIGl0IGlzIGFsc28gdXNlZCBmb3IgdGhlIG5hbWVHcm91cCBvdXRwdXQgb2YgdGhpcyBmdW5jdGlvbgogICAgbmFtZUNlbGwgPSAicHJlZGljdGVkQ2VsbF9VbiIsICMgbmFtZSB0aGUgY2VsbENvbERhdGEgZm9yIHRoZSAKICAgICMgcHJlZGljdGVkIHNjUk5BLXNlcSBjZWxsIGluIHRoZSBzcGVjaWZpZWQgQXJjaFJQcm9qZWN0IC0+CiAgICAjIHdpbGwgYWRkIGEgY29sdW1uIHRvIHRoZSBwcm9qZWN0CiAgICBuYW1lR3JvdXAgPSAicHJlZGljdGVkR3JvdXBfVW4iLAogICAgbmFtZVNjb3JlID0gInByZWRpY3RlZFNjb3JlX1VuIgopCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTV9CmRmIDwtIGFzX2RhdGFfZnJhbWUoY2JpbmQoZ2V0Q2VsbENvbERhdGEocHJvaiksIGdldEVtYmVkZGluZyhwcm9qKSkgKSAlPiUKICByZW5hbWUoYyh1bWFwMSA9IEl0ZXJhdGl2ZUxTSS5VTUFQX0RpbWVuc2lvbl8xLCB1bWFwMiAgPSBJdGVyYXRpdmVMU0kuVU1BUF9EaW1lbnNpb25fMikpCgpwMSA8LSBkZiAlPiUgZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSB1bWFwMSwgeSA9IHVtYXAyLCBjb2wgPSBwcmVkaWN0ZWRHcm91cF9VbiksIHNpemUgPSAuMDQpICsKICBndWlkZXMoY29sPWd1aWRlX2xlZ2VuZCh0aXRsZT0icHJlZGljdGVkIGNlbGwgdHlwZSIpKSArCiAgeGxhYigidW1hcDEiKSArCiAgeWxhYigidW1wYTIiKSArCiAgbGFicyh0aXRsZSA9ICJQcmVkaWN0ZWQgY2VsbCB0eXBlcyBhZnRlciB1bmNvbnN0cmFpbmVkIGludGVncmF0aW9uIikKCnAyIDwtIGRmICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHVtYXAxLCB5ID0gdW1hcDIsIGNvbCA9IHByZWRpY3RlZFNjb3JlX1VuKSwgc2l6ZSA9IC4wNCkgKwogIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPSJwcmVkaWN0aW9uIHNjb3JlIikpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2MoKSArCiAgeGxhYigidW1hcDEiKSArCiAgeWxhYigidW1wYTIiKSArCiAgbGFicyh0aXRsZSA9ICJQcmVkaWN0ZWQgY2VsbCB0eXBlcyBhZnRlciB1bmNvbnN0cmFpbmVkIGludGVncmF0aW9uIikKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMiwgbmNvbCA9IDEpCmBgYAoKCiMjIyBDb25zdHJhaW5lZCBpbnRlZ3JhdGlvbjoKCiogcHJpb3Iga25vd2xlZGdlIG9mIHRoZSBjZWxsIHR5cGVzIGlzIHVzZWQgdG8gbGltaXQgdGhlIHNlYXJjaCBzcGFjZSBvZiB0aGUgYWxpZ25tZW50Ci0+IGUuZy4gaWYgd2Uga25vdyB0aGF0IGNsdXN0ZXJzIEEsIEIsIEMgaW4gc2NBVEFDLXNlcSBkYXRhIGNvcnJlc3BvbmQgdG8gMwpkaWZmZXJlbnQgVCBjZWxsIGNsdXN0ZXJzIGFuZCBDbHVzdGVycyBYLCBZIGluIHNjUk5BLXNlcSBkYXRhIGNvcnJlc3BvbmQgdG8gCnR3byBUIGNlbGwgY2x1c3RlcnMsIHdlIGNvdWxkIHRyeSB0byBzcGVjaWZpY2FsbHkgYWxpZ24gdGhlc2UgY2VsbHMKCjEuIElkZW50aWZ5IHdoaWNoIGNlbGwgdHlwZXMgZnJvbSBzY1JOQS1zZXEgZGF0YSBhcmUgbW9zdCBhYnVuZGFudCBpbiBzY0FUQUMtc2VxIGNsdXN0ZXJzPwoyLiBjb25mdXNpb24gbWF0cml4IGlzIGNyZWF0ZWQgdGhhdCBsb29rcyBhdCB0aGUgaW50ZXJzZWN0aW9uIG9mIENsdXN0ZXJzIAphbmQgcHJlZGljdGVkIGdyb3VwIHdoaWNoIGNvbnRhaW5zIHRoZSBjZWxsIHR5cGVzIGFzIGlkZW50ZmllZCBieSBzY1JOQS1zZXEKMy4gVGhpcyBzaG93cyB3aGljaCBzY1JOQS1zZXEgY2VsbCB0eXBlIGlzIG1vc3QgYWJ1bmRhbnQgaW4gZWFjaCBvZiB0aGUgc2NBVEFDIGNsdXN0ZXJzCgpgYGB7cn0KIyBpbiB0aGUgY29uZnVzaW9uIG1hdHJpeCB0aGUgcm93cyBjb3JyZXNwb25kIHRvIGNsdXN0ZXJzIGluIHNjQVRBQyBkYXRhCiMgdGhlIGNvbHVtbnMgY29ycmVzcG9uZCB0byBjZWxsIHR5cGVzIGluIHNjUk5BLXNlcSBkYXRhCmNNIDwtIGFzLm1hdHJpeChjb25mdXNpb25NYXRyaXgocHJvaiRDbHVzdGVycywgcHJvaiRwcmVkaWN0ZWRHcm91cF9VbikpCmNNWywgMTo1XQoKcHJlQ2x1c3QgPC0gY29sbmFtZXMoY00pW2FwcGx5KGNNLCAxICwgd2hpY2gubWF4KV0KY2JpbmQocHJlQ2x1c3QsIHJvd25hbWVzKGNNKSkgI0Fzc2lnbm1lbnRzCmBgYAoKCgpXaGljaCBjZWxscyBpbiB0aGUgc2NSTkEtc2VxIGNvcnJlc3BvbmQgdG8gVGNlbGxzIGFuZCBOSyBjZWxscz8gCgpVc2UgcGF0dGVybiBtYXRjaGluZyBzdHJpbmdzIGluIGNvbWJpbmF0aW9uIHdpdGggZ3JlcCB0byBleHRyYWN0IHRoZSBzY0FUQUMtc2VxIApjbHVzdGVycyB0aGF0IGNvcnJlc3BvbmQgdG8gdGhlc2Ugc2NSTkEtc2VxIGNlbGwgdHlwZXMuCnwgYWN0cyBhcyBhbiBvciBzdGF0ZW1lbnQsIHNvIHdlIHNlYXJjaCBmb3IgYW55IHJvdyBpbiB0aGUgcHJlQ2x1c3QgY29sdW1uIG9mIHRoZQpjb25mdXNpb24gbWF0aXggdGhhdCBtYXRjaGVzIGEgc2NSTkEtc2VxIGNsdXN0ZXIgbnVtYmVyLgoKYGBge3J9CnVuaXF1ZShwcm9qJHByZWRpY3RlZEdyb3VwX1VuKQpgYGAKCmBgYHtyfQpjVE5LIDwtIHBhc3RlMChwYXN0ZTAoMTk6MjUpLCBjb2xsYXBzZSA9ICJ8IikKY1ROSwoKY05vblROSyA8LSBwYXN0ZTAoYyhwYXN0ZTAoIjAiLCAxOjkpLCAxMDoxMywgMTU6MTgpLCBjb2xsYXBzZSA9ICJ8IikKY05vblROSwpgYGAKCmBgYHtyfQpjbHVzdFROSyA8LSByb3duYW1lcyhjTSlbZ3JlcChjVE5LLCBwcmVDbHVzdCldCmNsdXN0VE5LCgpjbHVzdE5vblROSyA8LSByb3duYW1lcyhjTSlbZ3JlcChjTm9uVE5LLCBwcmVDbHVzdCldCmNsdXN0Tm9uVE5LCmBgYAoKSWRlbnRpZnkgc2NSTkEtc2VxIGNlbGxzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHNhbWUgY2VsbCB0eXBlcy4KCmBgYHtyfQojUk5BIGdldCBjZWxscyBpbiB0aGVzZSBjYXRlZ29yaWVzCnJuYVROSyA8LSBjb2xuYW1lcyhzZVJOQSlbZ3JlcChjVE5LLCBjb2xEYXRhKHNlUk5BKSRCaW9DbGFzc2lmaWNhdGlvbildCmhlYWQocm5hVE5LKQoKIyBnZXQgY2VsbCBub3QgaW4gdGhlc2UgY2F0ZWdvcmllcwpybmFOb25UTksgPC0gY29sbmFtZXMoc2VSTkEpW2dyZXAoY05vblROSywgY29sRGF0YShzZVJOQSkkQmlvQ2xhc3NpZmljYXRpb24pXQpoZWFkKHJuYU5vblROSykKYGBgCgoKV2UgY3JlYXRlIGEgbmVzdGVkIGxpc3Qgd2l0aCB0d28gdmVjdG9ycyBvZiBjZWxsIElEcwpgYGB7cn0KZ3JvdXBMaXN0IDwtIFNpbXBsZUxpc3QoCiAgICBUTksgPSBTaW1wbGVMaXN0KAogICAgICAgIEFUQUMgPSBwcm9qJGNlbGxOYW1lc1twcm9qJENsdXN0ZXJzICVpbiUgY2x1c3RUTktdLAogICAgICAgIFJOQSA9IHJuYVROSwogICAgKSwKICAgIE5vblROSyA9IFNpbXBsZUxpc3QoCiAgICAgICAgQVRBQyA9IHByb2okY2VsbE5hbWVzW3Byb2okQ2x1c3RlcnMgJWluJSBjbHVzdE5vblROS10sCiAgICAgICAgUk5BID0gcm5hTm9uVE5LCiAgICApICAgIAopCgpncm91cExpc3RbWzFdXVtbMV1dICU+JSBoZWFkCmBgYAoKYGBge3J9CnByb2ogPC0gYWRkR2VuZUludGVncmF0aW9uTWF0cml4KAogICAgQXJjaFJQcm9qID0gcHJvaiwgCiAgICB1c2VNYXRyaXggPSAiR2VuZVNjb3JlTWF0cml4IiwKICAgIG1hdHJpeE5hbWUgPSAiR2VuZUludGVncmF0aW9uTWF0cml4IiwKICAgIHJlZHVjZWREaW1zID0gIkl0ZXJhdGl2ZUxTSSIsCiAgICBzZVJOQSA9IHNlUk5BLAogICAgYWRkVG9BcnJvdyA9IEZBTFNFLCAKICAgIGdyb3VwTGlzdCA9IGdyb3VwTGlzdCwKICAgIGdyb3VwUk5BID0gIkJpb0NsYXNzaWZpY2F0aW9uIiwKICAgIG5hbWVDZWxsID0gInByZWRpY3RlZENlbGxfQ28iLAogICAgbmFtZUdyb3VwID0gInByZWRpY3RlZEdyb3VwX0NvIiwKICAgIG5hbWVTY29yZSA9ICJwcmVkaWN0ZWRTY29yZV9DbyIKKQpgYGAKCgpgYGB7cn0KcGFsIDwtIHBhbGV0dGVEaXNjcmV0ZSh2YWx1ZXMgPSBjb2xEYXRhKHNlUk5BKSRCaW9DbGFzc2lmaWNhdGlvbikKcGFsCmBgYAoKIyMjIENvbXBhcmUgdW5jb25zdHJhaW5lZCBhbmQgY29uc3RyYWluZWQgaW50ZWdyYXRpb24KClBsb3QgdGhlIGludGVncmF0aW9uIGZvciB1bmNvbnN0cmFpbmVkIChsZWZ0KSBhbmQgY29uc3RyYWluZWQgKHJpZ2h0KQppbnRlZ3JhdGlvbi4gV2Ugb3ZlcmxheSB0aGUgc2NSTkEtc2VxIGNlbGwgdHlwZXMgb24gdGhlIEFUQUMtc2VxIGRhdGEKYmFzZWQgb24gdGhlIHVuY29uc3RyYWluZWQvY29uc3RyYWluZWQgaW50ZWdyYXRpb24uIEluIHRoZSBwbG90IGJlbG93IHRoZSBkaWZmZXJlbmNlIGlzIHZlcnkgc3VidGxlLCBiZWNhdXNlIHRoZSBjZWxsIHR5cGVzIG9mIGludGVyZXN0IGFyZSB2ZXJ5IGRpc3RpbmN0LiBUaGVyZSBhcmUgc3VidGxlIGRpZmZlcmVuY2VzLCBmb3IgZXhhbXBsZSBpbiB0aGUgVCBjZWxsIGNsdXN0ZXJzLiBDb3VsZCB3ZSBhbHNvIHRyeSB0aGlzIGFwcHJvYWNoIHdpdGggY29uc3RyYWluaW5nIG90aGVyIGNlbGwgdHlwZXM/IENhbiB3ZSBjb21iaW5lIGRpZmZlcmVudCBjb25zdHJhaW5zL2dyb3Vwcz8KCgpgYGB7ciwgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9OH0KZGYgPC0gYXNfZGF0YV9mcmFtZShjYmluZChnZXRDZWxsQ29sRGF0YShwcm9qKSwgZ2V0RW1iZWRkaW5nKHByb2opKSApICU+JQogIHJlbmFtZShjKHVtYXAxID0gSXRlcmF0aXZlTFNJLlVNQVBfRGltZW5zaW9uXzEsIHVtYXAyICA9IEl0ZXJhdGl2ZUxTSS5VTUFQX0RpbWVuc2lvbl8yKSkKCnAxIDwtIGRmICU+JSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHVtYXAxLCB5ID0gdW1hcDIsIGNvbCA9IHByZWRpY3RlZEdyb3VwX1VuKSwgc2l6ZSA9IC4wNCkgKwogIGd1aWRlcyhjb2w9Z3VpZGVfbGVnZW5kKHRpdGxlPSJwcmVkaWN0ZWQgY2VsbCB0eXBlIikpICsKICB4bGFiKCJ1bWFwMSIpICsKICB5bGFiKCJ1bXBhMiIpICsKICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZCBjZWxsIHR5cGVzIGFmdGVyIHVuY29uc3RyYWluZWQgaW50ZWdyYXRpb24iKQoKcDIgPC0gZGYgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdW1hcDEsIHkgPSB1bWFwMiwgY29sID0gcHJlZGljdGVkU2NvcmVfVW4pLCBzaXplID0gLjA0KSArCiAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9InByZWRpY3Rpb24gc2NvcmUiKSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICB4bGFiKCJ1bWFwMSIpICsKICB5bGFiKCJ1bXBhMiIpICsKICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZCBjZWxsIHR5cGVzIGFmdGVyIHVuY29uc3RyYWluZWQgaW50ZWdyYXRpb24iKQoKcDMgPC0gZGYgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdW1hcDEsIHkgPSB1bWFwMiwgY29sID0gcHJlZGljdGVkR3JvdXBfQ28pLCBzaXplID0gLjA0KSArCiAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9InByZWRpY3RlZCBjZWxsIHR5cGUiKSkgKwogICNnZW9tX2xhYmVsKHByZWRpY3RlZEdyb3VwX0NvKSArCiAgeGxhYigidW1hcDEiKSArCiAgeWxhYigidW1wYTIiKSArCiAgbGFicyh0aXRsZSA9ICJQcmVkaWN0ZWQgY2VsbCB0eXBlcyBhZnRlciB1bmNvbnN0cmFpbmVkIGludGVncmF0aW9uIikKCnA0PC0gZGYgJT4lIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdW1hcDEsIHkgPSB1bWFwMiwgY29sID0gcHJlZGljdGVkU2NvcmVfQ28pLCBzaXplID0gLjA0KSArCiAgZ3VpZGVzKGNvbD1ndWlkZV9sZWdlbmQodGl0bGU9InByZWRpY3Rpb24gc2NvcmUiKSkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpICsKICB4bGFiKCJ1bWFwMSIpICsKICB5bGFiKCJ1bXBhMiIpICsKICBsYWJzKHRpdGxlID0gIlByZWRpY3RlZCBjZWxsIHR5cGVzIGFmdGVyIGNvbnN0cmFpbmVkIGludGVncmF0aW9uIikKCmdyaWRFeHRyYTo6Z3JpZC5hcnJhbmdlKHAxLCBwMywgbmNvbCA9IDEpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnAxIDwtIHBsb3RFbWJlZGRpbmcoCiAgICBwcm9qLCAKICAgIGNvbG9yQnkgPSAiY2VsbENvbERhdGEiLCAKICAgIG5hbWUgPSAicHJlZGljdGVkR3JvdXBfVW4iLCAKICAgIHBhbCA9IHBhbAopCgoKcDIgPC0gcGxvdEVtYmVkZGluZygKICAgIHByb2osIAogICAgY29sb3JCeSA9ICJjZWxsQ29sRGF0YSIsIAogICAgbmFtZSA9ICJwcmVkaWN0ZWRHcm91cF9DbyIsIAogICAgcGFsID0gcGFsCikKCgpnZ0FsaWduUGxvdHMocDEsIHAyLCB0eXBlID0gImgiKQpgYGAKCgoKIyMgQWRkaW5nIHBzZXVkby1zY1JOQS1zZXEgcHJvZmlsZXMgZm9yIGVhY2ggQVRBQy1zZXEgY2VsbAoKSWYgd2UgYXJlIGhhcHB5IHdpdGggdGhlIGludGVncmF0aW9uIHJlc3VsdHMgd2UgYWRkIHRoZW0gdG8gdGhlIEFycm93IEZpbGVzLiBUaGVuIHdlIGNhbiBjb21wYXJlIHRoZSBsaW5rZWQgZ2VuZSBleHByZXNzaW9uIHdpdGggdGhlIGluZmVycmVkIGdlbmUgZXhwcmVzc2lvbiBvYnRhaW5lZCB0aHJvdWdoIHRoZSBnZW5lIHNjb3Jlcy4gCgpgYGB7cn0KI341IG1pbnV0ZXMKcHJvaiA8LSBhZGRHZW5lSW50ZWdyYXRpb25NYXRyaXgoCiAgQXJjaFJQcm9qID0gcHJvaiwgCiAgdXNlTWF0cml4ID0gIkdlbmVTY29yZU1hdHJpeCIsCiAgbWF0cml4TmFtZSA9ICJHZW5lSW50ZWdyYXRpb25NYXRyaXgiLAogIHJlZHVjZWREaW1zID0gIkl0ZXJhdGl2ZUxTSSIsCiAgc2VSTkEgPSBzZVJOQSwKICBhZGRUb0Fycm93ID0gVFJVRSwKICBmb3JjZT0gVFJVRSwKICBncm91cExpc3QgPSBncm91cExpc3QsCiAgZ3JvdXBSTkEgPSAiQmlvQ2xhc3NpZmljYXRpb24iLAogIG5hbWVDZWxsID0gInByZWRpY3RlZENlbGwiLAogIG5hbWVHcm91cCA9ICJwcmVkaWN0ZWRHcm91cCIsCiAgbmFtZVNjb3JlID0gInByZWRpY3RlZFNjb3JlIiwKICAKKQoKCmdldEF2YWlsYWJsZU1hdHJpY2VzKHByb2opCgpwcm9qIDwtIGFkZEltcHV0ZVdlaWdodHMocHJvaikKYGBgCgoKCmBgYHtyfQptYXJrZXJHZW5lcyAgPC0gYygKICAiQ0QzNCIsICNFYXJseSBQcm9nZW5pdG9yCiAgIkdBVEExIiwgI0VyeXRocm9pZAogICJQQVg1IiwgIk1TNEExIiwgI0ItQ2VsbCBUcmFqZWN0b3J5CiAgIkNEMTQiLCAjTW9ub2N5dGVzCiAgIkNEM0QiLCAiQ0Q4QSIsICJUQlgyMSIsICJJTDdSIiAjVENlbGxzCikKCiMgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmcm9tIEdlbmVJbnRlZ3JhdGlvbk1hdHJpeApwMSA8LSBwbG90RW1iZWRkaW5nKAogIEFyY2hSUHJvaiA9IHByb2osIAogIGNvbG9yQnkgPSAiR2VuZUludGVncmF0aW9uTWF0cml4IiwgCiAgbmFtZSA9IG1hcmtlckdlbmVzLCAKICBjb250aW51b3VzU2V0ID0gImhvcml6b25FeHRyYSIsCiAgZW1iZWRkaW5nID0gIlVNQVAiLAogIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKCgojIEdlbmUgc2NvcmUgdmFsdWVzIGZyb20gR2VuZVNjb3JlTWF0cml4CgpwMiA8LSBwbG90RW1iZWRkaW5nKAogIEFyY2hSUHJvaiA9IHByb2osIAogIGNvbG9yQnkgPSAiR2VuZVNjb3JlTWF0cml4IiwgCiAgY29udGludW91c1NldCA9ICJob3Jpem9uRXh0cmEiLAogIG5hbWUgPSBtYXJrZXJHZW5lcywgCiAgZW1iZWRkaW5nID0gIlVNQVAiLAogIGltcHV0ZVdlaWdodHMgPSBnZXRJbXB1dGVXZWlnaHRzKHByb2opCikKYGBgCgpJbiB0aGUgcGxvdCBiZWxvdyB5b3UgY2FuIHNlZSB0aGUgZ2VuZSBleHByZXNzaW9uIHZhbHVlcyBmcm9tCmludGVncmF0aW9uIHdpdGggdGhlIHNjUk5BLXNlcSBkYXRhLgoKYGBge3J9CnAxYyA8LSBsYXBwbHkocDEsIGZ1bmN0aW9uKHgpewogIHggKyBndWlkZXMoY29sb3IgPSBGQUxTRSwgZmlsbCA9IEZBTFNFKSArIAogICAgdGhlbWVfQXJjaFIoYmFzZVNpemUgPSA2LjUpICsKICAgIHRoZW1lKHBsb3QubWFyZ2luID0gdW5pdChjKDAsIDAsIDAsIDApLCAiY20iKSkgKwogICAgdGhlbWUoCiAgICAgIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCksIAogICAgICBheGlzLnRleHQueT1lbGVtZW50X2JsYW5rKCksIAogICAgICBheGlzLnRpY2tzLnk9ZWxlbWVudF9ibGFuaygpCiAgICApIAp9KQoKcDJjIDwtIGxhcHBseShwMiwgZnVuY3Rpb24oeCl7CiAgeCArIGd1aWRlcyhjb2xvciA9IEZBTFNFLCBmaWxsID0gRkFMU0UpICsgCiAgICB0aGVtZV9BcmNoUihiYXNlU2l6ZSA9IDYuNSkgKwogICAgdGhlbWUocGxvdC5tYXJnaW4gPSB1bml0KGMoMCwgMCwgMCwgMCksICJjbSIpKSArCiAgICB0aGVtZSgKICAgICAgYXhpcy50ZXh0Lng9ZWxlbWVudF9ibGFuaygpLCAKICAgICAgYXhpcy50aWNrcy54PWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIGF4aXMudGV4dC55PWVsZW1lbnRfYmxhbmsoKSwgCiAgICAgIGF4aXMudGlja3MueT1lbGVtZW50X2JsYW5rKCkKICAgICkgCn0pCmRvLmNhbGwoY293cGxvdDo6cGxvdF9ncmlkLCBjKGxpc3QobmNvbCA9IDMpLCBwMWMpKQpkby5jYWxsKGNvd3Bsb3Q6OnBsb3RfZ3JpZCwgYyhsaXN0KG5jb2wgPSAzKSwgcDJjKSkKCmBgYAoKSW4gdGhlIHBsb3QgYmVsb3cgeW91IGNhbiBzZWUgdGhlIGdlbmUgc2NvcmUgdmFsdWVzIHdoaWNoIHdlIGNhbGN1bGF0ZQphYm92ZS4gV2hlbiBjb21wYXJpbmcgdGhlc2UgdmFsdWVzIHdpdGggdGhlIGdlbmUgZXhwcmVzc2lvbiBmcm9tIGFib3ZlIHdlIApjYW4gc2VlIHRoYXQgdGhlIGdlbmUgc2NvcmVzIGNhbGN1bGF0ZWQgZnJvbSBzY0FUQUMtc2VxIGFyZSBtb3JlIHNlbnNpdGl2ZSBpbiAKY29tcGFyaXNvbi4gVGhpcyBtZWFucyB0aGF0IHNvbWUgcmVnaW9ucyBhcmUgb3BlbiAocHJpbWVkKSwgYnV0IG5vdCB5ZXQgCnRyYW5zY3JpYmVkLiBUaGlzIGlzIGFuIGluZm9ybWF0aW9uIHRoYXQgeW91IGNhbiBvbmx5IGdldCB3aXRoIG11bHRpLW9taWNzIGRhdGEuICAgCgpgYGB7cn0KZG8uY2FsbChjb3dwbG90OjpwbG90X2dyaWQsIGMobGlzdChuY29sID0gMyksIHAyYykpCmBgYAoKClRoZSByZXN1bHRzIGZyb20gdGhlIHR3byBkaWZmZXJlbnQgbWV0aGRvcyBmb3IgaW5mZXJyaW5nIGdlbmUgZXhwcmVzc2lvbiBhcmUgc2ltaWxhciwgYnV0IG5vdCBpZGVudGljYWwuIAoKCgoKYGBge3IgY2xhc3Muc291cmNlID0gJ2ZvbGQtc2hvdyd9CmNtIDwtIGNvbmZ1c2lvbk1hdHJpeChwcm9qJENsdXN0ZXJzLCBwcm9qJHByZWRpY3RlZEdyb3VwKQpjbVsxOjEwLCAxOjVdCmRpbShjbSkKYGBgCgpgYGB7cn0KIyBmb3IgZWFjaCByb3cgZ2V0IHRoZSBpbmRleCBvZiB0aGUgbWF4aW11bSA9IHByZWRpY3RlZAojIGdyb3VwIHdoaWNoIGJlc3QgZGVmaW5lcyBhIGNsc3V0ZXIKYXBwbHkoY20sIDEsIHdoaWNoLm1heCkKCiMgdXNpbmcgdGhlc2UgaW5kaWNlcyBleHRyYWN0IHRoZSBjb3JyZXNwb25kaW5nIHByZWRpY3RlZCBjZWxsIHR5cGUKIyB3ZSB3aWxsIHVzZSB0aGVzZSBwcmVkaWN0ZWQgY2VsbCB0eXBlcyBhcyBvdXIgbmV3IGNsdXN0ZXIgbGFiZWxzCmxhYmVsTmV3IDwtIGNvbG5hbWVzKGNtKVthcHBseShjbSwgMSwgd2hpY2gubWF4KV0KbGFiZWxOZXcKYGBgCgpSZW5hbWUgdGhlIHNjUk5BLXNlcSBjbHVzdGVyIGxhYmVscyB0byBzb21ldGhpbmcgbW9yZSBpbnRlcnByZXRhYmxlOgoKYGBge3J9CnJlbWFwQ2x1c3QgPC0gYygKICAgICIwMV9IU0MiID0gIlByb2dlbml0b3IiLAogICAgIjAyX0Vhcmx5LkVyeXRoIiA9ICJFcnl0aHJvaWQiLAogICAgIjAzX0xhdGUuRXJ5dGgiID0gIkVyeXRocm9pZCIsCiAgICAiMDRfRWFybHkuQmFzbyIgPSAiQmFzb3BoaWwiLAogICAgIjA1X0NNUC5MTVBQIiA9ICJQcm9nZW5pdG9yIiwKICAgICIwNl9DTFAuMSIgPSAiQ0xQIiwKICAgICIwN19HTVAiID0gIkdNUCIsCiAgICAiMDhfR01QLk5ldXQiID0gIkdNUCIsCiAgICAiMDlfcERDIiA9ICJwREMiLAogICAgIjEwX2NEQyIgPSAiY0RDIiwKICAgICIxMV9DRDE0Lk1vbm8uMSIgPSAiTW9ubyIsCiAgICAiMTJfQ0QxNC5Nb25vLjIiID0gIk1vbm8iLAogICAgIjEzX0NEMTYuTW9ubyIgPSAiTW9ubyIsCiAgICAiMTVfQ0xQLjIiID0gIkNMUCIsCiAgICAiMTZfUHJlLkIiID0gIlByZUIiLAogICAgIjE3X0IiID0gIkIiLAogICAgIjE4X1BsYXNtYSIgPSAiUGxhc21hIiwKICAgICIxOV9DRDguTiIgPSAiQ0Q4Lk4iLAogICAgIjIwX0NENC5OMSIgPSAiQ0Q0Lk4iLAogICAgIjIxX0NENC5OMiIgPSAiQ0Q0Lk4iLAogICAgIjIyX0NENC5NIiA9ICJDRDQuTSIsCiAgICAiMjNfQ0Q4LkVNIiA9ICJDRDguRU0iLAogICAgIjI0X0NEOC5DTSIgPSAiQ0Q4LkNNIiwKICAgICIyNV9OSyIgPSAiTksiCikKcmVtYXBDbHVzdCA8LSByZW1hcENsdXN0W25hbWVzKHJlbWFwQ2x1c3QpICVpbiUgbGFiZWxOZXddCnJlbWFwQ2x1c3QKYGBgCgpVc2luZyBgbWFwTGFiZWxzKClgIHlvdSBjYW4gY29udmVydCB0aGUgbGFiZWxzIHRvIHRoZSBuZXcgc3lzdGVtCgpgYGB7cn0KbGFiZWxOZXcyIDwtIG1hcExhYmVscyhsYWJlbE5ldywgb2xkTGFiZWxzID0gbmFtZXMocmVtYXBDbHVzdCksIG5ld0xhYmVscyA9IHJlbWFwQ2x1c3QpCmxhYmVsTmV3MgpgYGAKCmBgYHtyfQpwcm9qJENsdXN0ZXJzMiA8LSBtYXBMYWJlbHMocHJvaiRDbHVzdGVycywgbmV3TGFiZWxzID0gbGFiZWxOZXcyLCBvbGRMYWJlbHMgPSB1bmlxdWUocHJvaiRDbHVzdGVycykpCmBgYAoKCmBgYHtyfQpwMSA8LSBwbG90RW1iZWRkaW5nKHByb2osIGNvbG9yQnkgPSAiY2VsbENvbERhdGEiLCBuYW1lID0gIkNsdXN0ZXJzMiIpCnAxCmBgYAoKQWxsIG9mIHRoZSBhYm92ZSBhbmFseXNpcyBpcyBhbHNvIGEgZ3JlYXQgc3RhcnRpbmcgcG9pbnQgZm9yIG1vcmUgY29tcGxleCBnZW5lIHJlZ3VsYXRpb24gYW5hbHlzaXMuCgoKYGBge3J9CnNhdmVBcmNoUlByb2plY3QocHJvaiwgb3V0cHV0RGlyZWN0b3J5ID0gIlNhdmVfVmlnbmV0dGVfb2JqZWN0XzEiLCBsb2FkID0gRkFMU0UpCmBgYAoK